package GraphicView;

import ikelib.ChannelData;
import ikelib.MapUtil;
import ikelib.Median;
import ikelib.drawTools;
import ikelib.wintime;

import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
//import java.awt.geom.Ellipse2D;
import java.io.*;
import java.lang.reflect.Array;
import java.net.DatagramPacket;
import java.net.URL;
//import java.net.DatagramSocket;
//import java.net.SocketException;
import java.sql.Time;
import java.util.*;
import java.util.List;
import java.util.Map.Entry;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;

import wcg.ChannelConfig;
import wcg.ChannelDataCollection;
import wcg.ChannelDataDisplayProperty;
import wcg.ChannelOutlineData;
import wcg.ChannelOutlineDataCollection;
import wcg.CsvOptionDialog;
import wcg.CsvSigmaOptionDialog;
import wcg.DataTableViewDialog;
import wcg.DialogChannelDisplayProperty;
import wcg.DialogSingleChannelAmplitudeProperty;
import wcg.DialogTimeMarker;
import wcg.DialogTimeSetting;
import wcg.DialogYFixScale;
import wcg.DlgChangeChannelID;
import wcg.KNET_ascii;
import wcg.OptioncommandInterface;
import wcg.Optionconsole;
import wcg.WinDataBaseCallBack;
import wcg.setStartTime;
import wcg.winDataBase;
import wcg.winchkgProperty;
/**
 *　振幅-時間　グラフを描画する
 * @author ikechan
 *
 */
public class GraphicView extends JPanel implements DropTargetListener,MouseListener,
	ActionListener ,MouseMotionListener,ItemListener ,
	Printable,OptioncommandInterface,WinDataBaseCallBack ,KeyListener,FocusListener,setStartTime {
	
	private DataTableViewDialog mDataTableViewDialog = null;
	private JFrame pArentFrame = null;
	private GraphicView mYSelf = null;
	private boolean DispChannelNumber = true;
	private float GraphHeight=10;
	private static int IndividualUpDownButtonMinimum = 6;
	public boolean ClipRect = true;
	
	
	private int DrawMode = 0;
	private int DrawPoint = 0;
	private String CSVSigmaOutputResultMessage = new String();
	private boolean ConvSigmaSNR=false;

	private int		DispData3PreYmax=0;
	private int		DispData3PreYmin=0;
	private boolean DispData3FromMax=false;
	private int 	DispData3LastX=0;
	
	private int     DispData3LastYmax;
	private int     DispData3LastYmin;
	private boolean CsvDispTime = true;
	private boolean CsvDispHeader = true;
	private boolean CsvMillisecondComma = false;
	private boolean LittleEndian = true;
	private boolean IsPrint = false;
	//public GraphicViewPanel ParentPanel = new GraphicViewPanel() ;
	public GraphicViewPanel ParentPanel = null ;
	public static final int COLOR_BW_WHITE_BACK = 0;
	public static final int COLOR_BW_BLACK_BACK = 1;
	public static final int COLOR_COLOR_BLACK_BACK = 2;
	public static final int COLOR_COLOR_WHITE_BACK = 3;
	private int ColorMode = COLOR_BW_WHITE_BACK;
	
	public static final int COLOR_AUTO_BY_CHANNEL = 0;
	public static final int COLOR_AUTO_BY_STATION = 1;
	public static final int COLOR_AUTO_BY_COMPONENT = 2;
	private int ColorCalcMode = COLOR_AUTO_BY_CHANNEL;
	public boolean AutoDisplay=true;
	
	public int UdpRetryCounter = 0;
	public int UdpRetryRecvCounter = 0;
	private  int ChFontDisplay = 16;
	private static final int CH_DISPLAY_MAX = 1000;
	private static final String INIT_FILE = "Channel.ini";
	private boolean AutoTrack;
	
	private int PacketCounter = 0;
	JPopupMenu PopupMenuTime = new JPopupMenu();
	JPopupMenu PopupMenuDrum = new JPopupMenu();

	JPopupMenu PopupMenuAmp = new JPopupMenu();

	
	private static final long serialVersionUID = 1L;
	//private winDataBase DataBase = new winDataBase();
	private winDataBase DataBase = null;
	private wintime GVDispTimeStart = new wintime();
	
	private long GVDispTimeRange_ms=1000;
	
	private Rectangle AreaXaxis  =new Rectangle();
	private Rectangle AreaYaxis  =new Rectangle();
	private Rectangle AreaGraph  =new Rectangle();
	private Rectangle AreaMisc  =new Rectangle();
	private Rectangle XGuide = new Rectangle();
	
	private Rectangle DebugConsole = new Rectangle();
	private Rectangle AreaGraphHandle = new Rectangle(); 
	private Rectangle AreaTimeAxis = new Rectangle();
	private Rectangle XGuideViewRange = new Rectangle();
	
	private int		  	GraphNum=0;
	
	private int 		ChannelMax=0;
	
	private boolean		ChannelNumChangeFlag=true;
	private DialogYFixScale FixScaleDialog;
	private DialogSingleChannelAmplitudeProperty ChannelPropDialog;
	private DialogChannelDisplayProperty DlgChannelDisplayProperty;
	private CsvOptionDialog 	CsvOptionDlg=null;
	private CsvSigmaOptionDialog CsvSigmaOptionDialog=null;
	private Point CurrentPopupPosition = new Point();
	private HashMap<Integer,ChannelDataDisplayProperty> ManualYScale = new HashMap<Integer,ChannelDataDisplayProperty>();
	private int CursorDisplay = 1;
	private Point CurrentMousePositon = new Point();
	private wintime CurrentMouseTime = new wintime();
	private int CurrentMouseChannelIndex = -1;
	private int CurrentMouseChannel = -1;
	private int[] DataChannel;
	private double[] Datamean;
	private double[] Datavariant;
	private int [] DisplayChannel;
	private int DisplayChannelNumber;
	private int[] DataSampleRate;
	
	private int TimeSpanDraggPhase = 0;
	private Point DraggStart = new Point();
	private Point DraggEnd = new Point();
	private long[] PaintTime = new long[100];
	private int PaintTimeIndex = 0;
	private long PaintTimeCurrent=0;
	private long PaintTimeStart=0;
	private long PaintTimeEnd;
	private ChannelConfig ChConfig = null;
	private Optionconsole Console=null;
	private Color ColorBackGround = Color.white;
	private Color ColorTimeSample = Color.orange;
	
	private Color ColorTime1Sec = Color.blue;
	private Color ColorTime1min = Color.red;
	private Color ColorTime1hour = Color.black;
	private Color ColorXAxis = Color.black;
	private Color ColorYAxis = Color.black;
	
	private Color ColorXDragging = new Color(100,100,100,50);
	private Color ColorTextInfo = Color.black;
	
	private Color ColorCursor = Color.orange;
	private Color ColorDataBW = Color.gray;
	private Color ColorMarker0 = Color.red;
	private Color ColorMarker1 = Color.blue;
	
	private int CurrentDPI=96;
	private int ColorBrightness = 2;
	//private float ColorSaturate = 0.8f;
	private String ExportFolder = new String(".");
	private boolean ShowChannelInfo = true;
	private boolean ShowTimeSamplePoint = true;	// 時刻、サンプルポイントを描くかどうか
	private boolean DrawMean = false;
	private int XaxisAreaHeight = 50; // ｘ軸描画エリアの高さ
	private int XguideAreaHeight = 15; // x軸ガイドエリアの高さ
	private boolean DrawYLabel = true;	// Y軸のラベルを描くかどうか
	private boolean CanDrawNumber ;		// Y軸のラベルをかける高さがあるかどうか
	private boolean DrawYAxisLabel = true;	// Y軸のラベルとY軸を描くかどうか		
	private DialogTimeSetting DialogTmSet = null;	// 	時刻の直接設定
	private DialogTimeMarker DialogTmMarker = null; //  時刻マーカ操作
	private TimeMarker CurTimeMarker=null;		// ドラッグ中のタイムマーカ
	
	public boolean DrawCursorTimeInfo = true;  // カーソル時刻を描画するかどうか


	private int TimeMarkerDraggPhase = 0;		
	private float[] Dasharray1 = {2.0f,2.0f}; // 点線
	private BasicStroke Dashed1 = new BasicStroke(1.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER, 10.0f, Dasharray1 , 0.0f);
	private BasicStroke BoldStroke2 = new BasicStroke(2.0f);
	
	private int DrawCounter=0;
	
	
	/**
	 * タイムマーカ管理
	 * @author ikechan
	 *
	 */
	private class TimeMarker{
		public int Number=0;
		public int Xpos=0;
		public int Yline=-1;
		public Rectangle IconRect = null;
	}
	private ArrayList<TimeMarker> TmMarker = new ArrayList<TimeMarker>(); 	// 時刻マーカ
	
	//private boolean CursorOut=false;
	
	private int GraphAreaXOffset = 250;
	
	private boolean ShowChannelNumber = true;
	private boolean ShowStation = true;
	private boolean ShowComponent = true;
	
	//-------------------------------------------------------------------------------------
	public  final int GRAPHIC_TYPE_NORMAL = 0;
	public  final int GRAPHIC_TYPE_DRUMRECORDER = 1;
	public  final int GRAPHIC_TYPE_NORMAL_OVERLAP = 2;
	public  final int GRAPHIC_TYPE_END = 2;
	private int GraphType = GRAPHIC_TYPE_NORMAL;	// 	0:標準、1:ドラム記録形（複数行） 、2:標準の重ね書き
	/**
	 * グラフタイプを取得　
	 * 	GRAPHIC_TYPE_NORMAL = 0;
	 *  GRAPHIC_TYPE_DRUMRECODER = 1;
	 * @return
	 */
	public int getGraphType() {
		return GraphType;
	}
	/**
	 * グラフタイプをセット
	 * 	GRAPHIC_TYPE_NORMAL = 0;
	 *  GRAPHIC_TYPE_DRUMRECODER = 1;
	 * @param graphType
	 */
	public void setGraphType(int graphType) {
		if(GraphType != graphType){
			GraphType = graphType;
			if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
//				DrumTimeTrancate=false;
				drumTimeManagement(); 
//				DispTimeStart = DispTimeStartDrum ;
//				DispTimeRange_ms = DispTimeRange_msDrum;
			}
			
			repaintAll();
		}
	}
	//-------------------------------------------------------------------------------------
	//-------------------------------------------------------------------------------------
	//-------------------------------------------------------------------------------------
	//-------------------------------------------------------------------------------------
	/**
	 * Channel番号を描画するかどうかを得る
	 * @return
	 */
	public boolean getShowChannelNumber() {
		return ShowChannelNumber;
	}
	/**
	 * ステーション名を描画するかどうかを得る
	 * @return
	 */
	public boolean getShowStation() {
		return ShowStation;
	}
	/**
	 * 成分名を描画するかどうかを得る
	 * @return
	 */
	public boolean getShowComponent() {
		return ShowComponent;
	}
	/**
	 * channel番号を表示
	 * @param showChannelNumber
	 */
	public void setShowChannelNumber(boolean showChannelNumber) {
		ShowChannelNumber = showChannelNumber;
	}
	/**
	 * ステーション名を表示
	 * @param showStation
	 */
	public void setShowStation(boolean showStation) {
		ShowStation = showStation;
	}
	/**
	 * 成分名を表示
	 * @param showComponent
	 */
	public void setShowComponent(boolean showComponent) {
		ShowComponent = showComponent;
	}
	/**
	 * Y軸を描くかどうか
	 * @return 
	 */
	public boolean getDrawYAxisLabel(){
		return DrawYAxisLabel;
	}
	/**
	 * Y軸のラベルを描くかどうか 
	 * @param set
	 */
	public void setDrawYAxisLabel(boolean set){
		if(DrawYAxisLabel != set){
			DrawYAxisLabel = set;
		}
	}
	/**
	 * グラフ領域を広げる
	 * @param width
	 * ピクセル単位
	 */
	public void incGraphX(int width){
		GraphAreaXOffset+=width;
		repaintAll();
	}
	/**
	 * グラフ領域を狭める
	 * @param width
	 * ピクセル単位
	 */
	public void decGraphX(int width){
		if(GraphAreaXOffset-width>10){
			GraphAreaXOffset -= width;
			repaintAll();
		}
	}
	
	private DialogTimeMarker getDialogTimeMarker(){
		if(DialogTmMarker==null){
			DialogTmMarker = new DialogTimeMarker(getParentFrame());
			DialogTmMarker.setParent(mYSelf);
			DialogTmMarker.setLocation(mYSelf.getLocationOnScreen());
			ImageIcon icon = new ImageIcon(getClass().getClassLoader().getResource("images/marker.png"));
			DialogTmMarker.setIconImage(icon.getImage());		
		}
		return DialogTmMarker;
	}
	private DialogTimeSetting getDialogTimeSetting(){
		if(DialogTmSet==null){
			DialogTmSet = new DialogTimeSetting(getParentFrame());
			DialogTmSet.setParent(mYSelf);
			DialogTmSet.setLocation(mYSelf.getLocationOnScreen());
			ImageIcon icon = new ImageIcon(getClass().getClassLoader().getResource("images/starttime.png"));
			DialogTmSet.setIconImage(icon.getImage());		
		}
		return DialogTmSet;
	}
	
	
	/**
	 * コンストラクタ
	 */
	final GraphicView gv=this;
	GraphicView(){
		//this.setSize(getPreferredSize());
		//this.setOpaque(true);
		addMouseListener(this);
		addMouseMotionListener(this);
		final GraphicView gv=this;
		mYSelf = this;
		
		//-----------------------------------------------------------------------------
		// 時間軸のコンテキストメニュー
		addPopupMenuItem(PopupMenuTime,"Time Marker", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				getDialogTimeMarker().setVisible(true);
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/marker.png")));		
		PopupMenuTime.addSeparator();
		addPopupMenuItem(PopupMenuTime,"Set Start Time", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doTimeSetDialog();
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/starttime.png")));
		
		PopupMenuTime.addSeparator();
		addPopupMenuItem(PopupMenuTime,"Copy Marker Average(m)to clipboard", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doMarkerClipBoard();
			}
	    });
		
		PopupMenuTime.addSeparator();
		addPopupMenuItem(PopupMenuTime,"Shift 1sec > ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.inc_sec();
				onDispTimeChange();
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuTime,"Shift 1min > ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.inc_sec(60);
				onDispTimeChange();
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuTime,"Shift 1hour > ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.inc_sec(60*60);
				onDispTimeChange();
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuTime,"Shift 1day > ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.inc_sec(60*60*24);
				onDispTimeChange();
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuTime,"Shift 1sec < ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.dec_sec();
				onDispTimeChange();
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuTime,"Shift 1min < ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.dec_sec(60);
				onDispTimeChange();
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuTime,"Shift 1hour < ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.dec_sec(60*60);
				onDispTimeChange();
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuTime,"Shift 1day < ", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				GVDispTimeStart.dec_sec(60*60*24);
				onDispTimeChange();
				repaintAll();
			}
	    });

		
		//-----------------------------------------------------------------------------
		// 振幅軸のコンテキストメニュー
		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Auto Scale(all channel)", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					doAutoScale();
				}
		    });
			addPopupMenuItem(PopupMenuAmp,"Single Scale(all channel)", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					doSingleScale();
				}

		    });
			
		}
		addPopupMenuItem(PopupMenuAmp,"Manual Scale", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doManualScale();
				//
//				if(FixScaleDialog != null){
//					if(FixScaleDialog.isShowing()){
//						FixScaleDialog.dispose();
//					}
//				}
//				//FixScaleDialog = new DialogYFixScale((Frame)(gv.getParent().getParent()));
//				FixScaleDialog = new DialogYFixScale(getParentFrame());
//				FixScaleDialog.setParent(gv);
//
//				//Point p = getParent().getParent().getLocation();
//				Point p = getParentPoint();
//				FixScaleDialog.setLocation(100+p.x+CurrentPopupPosition.x, 100+p.y+CurrentPopupPosition.y);
//				
//				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
//				if(pro != null){
//					pro.mode = ChannelDataDisplayProperty.MODE_FIX;
//					ManualYScale.put(pro.getChannel(),pro);
//					ChConfig.setData(pro.getChannel(), pro);
//					FixScaleDialog.CenterValue  = (int)(pro.getCenterValue());
//					FixScaleDialog.AmplitudeValue = (pro.getAmplitudeValue());
//					FixScaleDialog.channel = pro.getChannel();
//					FixScaleDialog.OffsetValue = pro.offset;
//					FixScaleDialog.Unit = pro.unitString;
//					FixScaleDialog.Factor = pro.unitFactor;
//					FixScaleDialog.FullScale = pro.FullScale;
//					FixScaleDialog.UnitType = pro.unitType;
//					FixScaleDialog.setparam();
//					FixScaleDialog.setVisible(true);
//				}				
//				repaintAll();
			}
	    });
		if(winchkgProperty.availableTimechartManiac){
			PopupMenuAmp.addSeparator();
			addPopupMenuItem(PopupMenuAmp,"Offset Scale ON (all channel)", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					doScaleOffsetAll(true);
				}
	
		    });
			addPopupMenuItem(PopupMenuAmp,"Offset Scale OFF(all channel)", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					doScaleOffsetAll(false);
				}
	
		    });
			PopupMenuAmp.addSeparator();
		}

		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Reset Scale", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
					if(pro != null){
						pro.mode = ChannelDataDisplayProperty.MODE_AUTO;
					}				
					repaintAll();
				}
		    });
			addPopupMenuItem(PopupMenuAmp,"Full Scale", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
					if(pro != null){
						//pro.setMaxMin(Integer.MAX_VALUE, Integer.MIN_VALUE);
						pro.setMaxMin(pro.FullScale-1L, -pro.FullScale);
						pro.offset=0;
					}				
					repaintAll();
				}
		    });
			PopupMenuAmp.addSeparator();
		}
		addPopupMenuItem(PopupMenuAmp,"Hide this channel", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
				if(pro != null){
					pro.hide=1;
					ManualYScale.put(pro.getChannel(),pro);
					ChConfig.setData(pro.getChannel(), pro);
				}				
				repaintAll();
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/hide.png")));	
	
		
		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Show all(max) channel", new ActionListener(){ 
	
				public void actionPerformed(ActionEvent e) {
					showAllChannel();
				}
	
		    });
			addPopupMenuItem(PopupMenuAmp,"Hide other channel", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
					if(pro != null){
						hideAllChannel(pro.getChannel());
					}
					else {
						hideAllChannel(-1);
					}
				}
	
		    },new ImageIcon(getClass().getClassLoader().getResource("images/hideall.png")));	
			PopupMenuAmp.addSeparator();
		}

		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Copy Average(m),Unbiased Variance(u) to clipboard", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					doCopyClipBoard();
				}
		    });
			addPopupMenuItem(PopupMenuAmp,"Copy Marker Average(m)to clipboard", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					doMarkerClipBoard();
				}
		    });
			PopupMenuAmp.addSeparator();
		}
		addPopupMenuItem(PopupMenuAmp,"Configure Digit/Physical Quantity", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				digitPhysicalQuantity();
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/config.png")));
		addPopupMenuItem(PopupMenuAmp,"Digit (all channel)", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doDigitPhysicalAll(false);
			}

	    },new ImageIcon(getClass().getClassLoader().getResource("images/123.png")));
		addPopupMenuItem(PopupMenuAmp,"Physical Quantity (all channel)", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doDigitPhysicalAll(true);
			}

	    },new ImageIcon(getClass().getClassLoader().getResource("images/m-s.png")));
		PopupMenuAmp.addSeparator();
		
		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Change Channel ID", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					onChangeChannelID(CurrentPopupPosition);
				}
	
		    });
			PopupMenuAmp.addSeparator();
		}

		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Toggle u or SNR", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					setConvSigmaSNR(!isConvSigmaSNR());
				}
	
		    },new ImageIcon(getClass().getClassLoader().getResource("images/sn.png")));
			PopupMenuAmp.addSeparator();
		}
		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Configure channel", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					selectDisplayChannel(0);
				}
		    },new ImageIcon(getClass().getClassLoader().getResource("images/display_config_table.png")));
		}
		if(winchkgProperty.availableTimechartManiac){
				addPopupMenuItem(PopupMenuAmp,"View channel.ini", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					selectDisplayChannel(1);
				}
		    },new ImageIcon(getClass().getClassLoader().getResource("images/view_ini.png")));
			PopupMenuAmp.addSeparator();
			addPopupMenuItem(PopupMenuAmp,"Reset contents", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					resetContent();
				}
		    },new ImageIcon(getClass().getClassLoader().getResource("images/trashcan.png")));
			PopupMenuAmp.addSeparator();
		}
		if(winchkgProperty.availableTimechartManiac){
			addPopupMenuItem(PopupMenuAmp,"Google map...", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
					if(pro != null){
						if(pro.Latitude != 0.0 && pro.Longitude != 0.0){
							String title = String.format("%04X %s %s", pro.getChannel(),pro.getStation(),pro.getComponent());
							MapUtil.showMap(pro.Latitude, pro.Longitude, title);
						}
						else{
							JOptionPane.showMessageDialog(null, "Invalid Longitude or Latitude");
						}
					}				
				}
		    },new ImageIcon(getClass().getClassLoader().getResource("images/map.png")));
			addPopupMenuItem(PopupMenuAmp,"Google Earth...", new ActionListener(){ 
				public void actionPerformed(ActionEvent e) {
					ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
					if(pro != null){
						if(pro.Latitude != 0.0 && pro.Longitude != 0.0){
							String title = String.format("%04X %s %s", pro.getChannel(),pro.getStation(),pro.getComponent());
							MapUtil.showEarth(pro.Latitude, pro.Longitude,pro.Altitude, title);
						}
						else{
							JOptionPane.showMessageDialog(null, "Invalid Longitude or Latitude");
						}
					}				
				}
		    },new ImageIcon(getClass().getClassLoader().getResource("images/earth.png")));
		}
		//-----------------------------------------------------------------------------
		// DrumViewのコンテキストメニュー
		
		JMenu column = new JMenu("Column");
		
		addPopupMenuItem(column,"1", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumColNumberMax=1; 		repaintAll();
			}
	    });		
		addPopupMenuItem(column,"2", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumColNumberMax=2;			repaintAll();
			}
	    });		
		addPopupMenuItem(column,"3", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumColNumberMax=3;			repaintAll();
			}
	    });		
		addPopupMenuItem(column,"4", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumColNumberMax=4;			repaintAll();
			}
	    });		
		addPopupMenuItem(column,"5", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumColNumberMax=5;			repaintAll();
			}
	    });		
		addPopupMenuItem(column,"6", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumColNumberMax=6;			repaintAll();
			}
	    });		
		PopupMenuDrum.add(column);
		PopupMenuDrum.addSeparator();
		//------------------------------------------------
		JMenu span = new JMenu("Span");
		addPopupMenuItem(span,"1 minute", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan1Minute;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"3 minutes", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan3Minute;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"10 minutes", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan10Minute;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"30 minutes", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan30Minute;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"1 hour", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan1Hour;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"3 hours", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan3Hour;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"12 hours", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan12Hour;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"1 day", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan1Day;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"3 days", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan3Day;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		addPopupMenuItem(span,"10 days", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				DrumTimeSpanCode = DrumTimeSpan10Day;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
	    });		
		PopupMenuDrum.add(span);
		PopupMenuDrum.addSeparator();
		//------------------------------------------------
		
		
		addPopupMenuItem(PopupMenuDrum,"Auto Scale(all channel)", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doAutoScale();
			}
	    });
		addPopupMenuItem(PopupMenuDrum,"Single Scale(all channel)", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doSingleScale();
			}

	    });
		addPopupMenuItem(PopupMenuDrum,"Manual Scale", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				doManualScale();
//				if(FixScaleDialog != null){
//					if(FixScaleDialog.isShowing()){
//						FixScaleDialog.dispose();
//					}
//				}
//				//FixScaleDialog = new DialogYFixScale((Frame)(gv.getParent().getParent()));
//				FixScaleDialog = new DialogYFixScale(getParentFrame());
//				FixScaleDialog.setParent(gv);
//
//				//Point p = getParent().getParent().getLocation();
//				Point p = getParentPoint();
//				FixScaleDialog.setLocation(100+p.x+CurrentPopupPosition.x, 100+p.y+CurrentPopupPosition.y);
//				
//				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
//				if(pro != null){
//					pro.mode = ChannelDataDisplayProperty.MODE_FIX;
//					ManualYScale.put(pro.getChannel(),pro);
//					ChConfig.setData(pro.getChannel(), pro);
//					FixScaleDialog.CenterValue  = (int)(pro.getCenterValue());
//					FixScaleDialog.AmplitudeValue = (pro.getAmplitudeValue());
//					FixScaleDialog.channel = pro.getChannel();
//					FixScaleDialog.OffsetValue = pro.offset;
//					FixScaleDialog.Unit = pro.unitString;
//					FixScaleDialog.Factor = pro.unitFactor;
//					FixScaleDialog.FullScale = pro.FullScale;
//					FixScaleDialog.UnitType = pro.unitType;
//					FixScaleDialog.setparam();
//					FixScaleDialog.setVisible(true);
//				}				
//				repaintAll();
			}
	    });
		PopupMenuDrum.addSeparator();

		addPopupMenuItem(PopupMenuDrum,"Reset Scale", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
				if(pro != null){
					pro.mode = ChannelDataDisplayProperty.MODE_AUTO;
				}				
				repaintAll();
			}
	    });
		addPopupMenuItem(PopupMenuDrum,"Full Scale", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
				if(pro != null){
					//pro.setMaxMin(Integer.MAX_VALUE, Integer.MIN_VALUE);
					pro.setMaxMin(pro.FullScale-1, -pro.FullScale);
					pro.offset=0;
				}				
				repaintAll();
			}
	    });
		PopupMenuDrum.addSeparator();
		addPopupMenuItem(PopupMenuDrum,"Hide this channel", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
				if(pro != null){
					pro.hide=1;
					ManualYScale.put(pro.getChannel(),pro);
					ChConfig.setData(pro.getChannel(), pro);
				}
				repaintAll();
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/hide.png")));	
		addPopupMenuItem(PopupMenuDrum,"Show all(max) channel", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				showAllChannel();
			}

	    });	
		addPopupMenuItem(PopupMenuDrum,"Hide all channel", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
				if(pro != null){
					hideAllChannel(pro.getChannel());
				}
				else {
					hideAllChannel(-1);
				}
			}

	    },new ImageIcon(getClass().getClassLoader().getResource("images/hideall.png")));	
		PopupMenuDrum.addSeparator();
		addPopupMenuItem(PopupMenuDrum,"Select Display channel", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				selectDisplayChannel(0);// Select display Channel
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/display_config_table.png")));
		addPopupMenuItem(PopupMenuDrum,"View channel.ini", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				selectDisplayChannel(1); // View channel.ini
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/view_ini.png")));
		PopupMenuDrum.addSeparator();
		addPopupMenuItem(PopupMenuDrum,"Reset contents", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				resetContent();
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/trashcan.png")));
		PopupMenuDrum.addSeparator();
		addPopupMenuItem(PopupMenuDrum,"Google map...", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
				if(pro != null){
					if(pro.Latitude != 0.0 && pro.Longitude != 0.0){
						String title = String.format("%04X %s %s", pro.getChannel(),pro.getStation(),pro.getComponent());
						MapUtil.showMap(pro.Latitude, pro.Longitude, title);
					}
					else{
						JOptionPane.showMessageDialog(null, "Invalid Longitude or Latitude");
					}
				}				
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/map.png")));
		addPopupMenuItem(PopupMenuDrum,"Google Earth...", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
				if(pro != null){
					if(pro.Latitude != 0.0 && pro.Longitude != 0.0){
						String title = String.format("%04X %s %s", pro.getChannel(),pro.getStation(),pro.getComponent());
						MapUtil.showEarth(pro.Latitude, pro.Longitude,pro.Altitude, title);
					}
					else{
						JOptionPane.showMessageDialog(null, "Invalid Longitude or Latitude");
					}
				}				
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/earth.png")));
		PopupMenuDrum.addSeparator();
		addPopupMenuItem(PopupMenuDrum,"Time Marker", new ActionListener(){ 
			public void actionPerformed(ActionEvent e) {
				getDialogTimeMarker().setVisible(true);
			}
	    },new ImageIcon(getClass().getClassLoader().getResource("images/marker.png")));		

		
		//-----------------------------------------------------------------------------
		//-----------------------------------------------------------------------------
		
		this.setLayout(null);
		AutoTrack = false;
		FixScaleDialog = new DialogYFixScale(null);
		ChannelPropDialog = new DialogSingleChannelAmplitudeProperty(null);
		DlgChannelDisplayProperty = new DialogChannelDisplayProperty(null);
		
			ChConfig = new ChannelConfig(INIT_FILE);
	//		ChConfig.load(INIT_FILE);
		intitChannelConfig();
		
		
		
		new DropTarget(this,this);
		
		
//		Console = new Optionconsole(null);
//		Console.setParent(this);
//		Console.setTitle(Console.getTitle()+"  Time-series");
		
		
		setColorMode(COLOR_COLOR_WHITE_BACK);
		
		addKeyListener(this);
		
//		DialogTmSet = new DialogTimeSetting(null);
//		DialogTmSet.setParent(this);
		
//		DialogTmMarker = new DialogTimeMarker(null);
//		DialogTmMarker.setParent(this);
		
		addFocusListener(this);
		
	}
	
	/**
	 * Channel IDを変更する
	 * @param currentPopupPosition2
	 */
	protected void onChangeChannelID(Point currentPopupPosition2) {
		DlgChangeChannelID dialog = new DlgChangeChannelID();
		//dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE  );

		ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
		if(pro!=null){
			dialog.ChannelIDOriginal = pro.getChannel();
		}
		dialog.ChannelIDNew = 0xff00;
		dialog.ChannelIDOffset = 0x1000;
		dialog.initDoSetParam();
		dialog.setVisible(true);
		if(dialog.Ok){
			if(dialog.SpecifyType==DlgChangeChannelID.TypeOneByOne){
				int id1 = dialog.ChannelIDOriginal;
				int id2 = dialog.ChannelIDNew;
				if(id1 != -1 && id2 != -1){
					DataBase.changeChannelID(id1,id2);
				}
			}
			else if(dialog.SpecifyType==DlgChangeChannelID.TYpeShiftAll){
				int ofs = dialog.ChannelIDOffset;
				DataBase.changeChannelID(ofs);
						
			}
		}
	}
	private void intitChannelConfig(){
		//ChConfig = new ChannelConfig(INIT_FILE);
		ChConfig.load(INIT_FILE);
	}
	
	
//	/**
//	 * 時間を表す文字列を得る
//	 * @param ms
//	 * ミリ秒単位
//	 * @return　String
//	 * 12msec , 23.500sec , ...
//	 */
//	private static String formatedTimeString(int ms){
//		if(ms < 1000){
//			return String.format("%,dmsec", ms);
//		}
//		else if(ms < 1000*60){
//			return String.format("%,6.3fsec",(float)ms/1000);
//		}
//		else if(ms < 1000*60*60){
//			return String.format("%,6.3fmin",(float)ms/1000/60);
//			
//		}
//		else if(ms < 1000*60*60*60){
//			return String.format("%,6.3fhour",(float)ms/1000/60/60);
//		}
//		return String.format("?");
//	}

	public void setConsole(Optionconsole c){
		Console = c;
	}
	
	@Override
	// ------------------------------------------
	// メニューやボタンなどのアクション
	public void actionPerformed(ActionEvent arg0) {
		Object sor = arg0.getSource();
		if(sor == ChannelPropDialog.jButtonOK){
			int id = ChannelPropDialog.ChID;
			if(id == -1){
				synchronized(DataBaseLock()){
					Iterator<Integer> ite = iteratorChannel();
					for(;ite.hasNext();){
						int ch = ite.next();
						if(!isChannelHide(ch)){
							setPU(ch);
						}
					}
				}
			}
			else{
				setPU(id);
			}
			ChannelPropDialog.dispose();
			ChConfig.save(INIT_FILE);
			repaintAll();
		}
		else if(sor == ChannelPropDialog.jButtonCancel){
			ChannelPropDialog.dispose();
		}
		// Ｃｈａｎｎｅｌ プロパティダイアログが閉じられたとき
		else if(sor == DlgChannelDisplayProperty.jButtonOK){
			if(DlgChannelDisplayProperty.getMode()==0){ // display config mode
				setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR )));
				int[] channel = DlgChannelDisplayProperty.getChannel();
				for(int i=0;i<channel.length;i++){
					boolean disp = DlgChannelDisplayProperty.isDisplay(i);
					boolean pu   = DlgChannelDisplayProperty.isPU(i);
					ChannelDataDisplayProperty pro = getChannelInfo(channel[i]);
					if(disp) pro.hide =0;
					else     pro.hide=1;
					if(pu) pro.unitType=ChannelDataDisplayProperty.UNIT_TYPE_CONV;
					else   pro.unitType=ChannelDataDisplayProperty.UNIT_TYPE_DIGIT;
					String s = DlgChannelDisplayProperty.getStation(i);
					pro.setStation(s);
					s = DlgChannelDisplayProperty.getChComponent(i);
					pro.setComponent(s);
					pro.setChannelTableIndex(DlgChannelDisplayProperty.getChannelTableIndex(i));
					
					pro.unitFactor = DlgChannelDisplayProperty.getFactor(i);
					pro.unitString = DlgChannelDisplayProperty.getUnit(i);
					pro.FullScale  = DlgChannelDisplayProperty.getFullScale(i);
					pro.Mask = DlgChannelDisplayProperty.getMask(i);
					pro.Latitude = DlgChannelDisplayProperty.getLatitude(i);
					pro.Longitude = DlgChannelDisplayProperty.getLongitude(i);
					pro.Altitude = DlgChannelDisplayProperty.getAltitude(i);
					ManualYScale.put(channel[i], pro);
					ChConfig.setData(channel[i], pro);
	
				}
				ChConfig.save(INIT_FILE);
				ArrayList<Integer> lst = DlgChannelDisplayProperty.getChannelSequence();
				DataBase.setChannelSequence(lst);
				ChannelNumChangeFlag=true;
				setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
				repaintAll();
			}
			// ＡＬＬ編集モードで終了したとき。
			else if(DlgChannelDisplayProperty.getMode()==1){
				if(JOptionPane.NO_OPTION !=  JOptionPane.showConfirmDialog(null, "Save to Channel.ini ?","Confirmation",JOptionPane.YES_NO_OPTION)){
					// TODO 時間はかかるけどなぜかウェイトカーソルは表示されない
					setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR )));
					ChConfig.save(INIT_FILE,DlgChannelDisplayProperty);
					ChConfig.load(INIT_FILE);
					setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
				}
			}
			DlgChannelDisplayProperty.dispose();
		}
		else if(sor == DlgChannelDisplayProperty.jButtonCANCEL){
			DlgChannelDisplayProperty.dispose();
		}
		// マニュアルスケールダイアログ
		else if(sor == FixScaleDialog.jButtonOK){
			getFixScaleDialogData();
			FixScaleDialog.dispose();
			repaintAll();
		}
		//else if(sor == FixScaleDialog.jButtonCancel){
		//	FixScaleDialog.dispose();
		//}
		else {
			JButton btn =  (JButton)arg0.getSource();
			String txt = btn.getText();
			String act = btn.getActionCommand();
			if(0 == txt.compareToIgnoreCase("AutoScale")){
				doAutoScale();
			}
			else if(0 == txt.compareToIgnoreCase("SingleScale")){
				doSingleScale();
			}
			else if(0 == act.compareToIgnoreCase("PanRight")){
				doIncTime();
			}
			else if(0 == act.compareToIgnoreCase("PanLeft")){
				doDecTime();
			}
			else if(0 == act.compareToIgnoreCase("PanRight2")){
				doPageIncTime();
			}
			else if(0 == act.compareToIgnoreCase("PanLeft2")){
				doPageDecTime();
			}
			else if(0 == act.compareToIgnoreCase("ChannelDown")){
				doChannelDown();
			}
			else if(0 == act.compareToIgnoreCase("ChannelUp")){
				doChannelUp();
			}
			else if(0 == act.compareToIgnoreCase("ChannelInc")){
				doChannelInc();
			}
			else if(0 == act.compareToIgnoreCase("ChannelDec")){
				doChannelDec();
			}
			else if(0 == act.compareToIgnoreCase("ZoomIn")){
				doZoomIn();
			}
			else if(0 == act.compareToIgnoreCase("ZoomOut")){
				doZoomOut();
			}
			else{
				// マニュアルスケールダイアログからのメッセージ
				getFixScaleDialogData();
				repaintAll();
			}
		}
	}
//	/**
//	 * channelを追加する
//	 * @param ch
//	 */
//	private void addChannel(int ch){
//		DataBase.addChannel(ch);
//	}
//------------------------------------------------
	// ＣＨデータを追加
	public void addChData(byte[] chdata){
		DataBase.addChData(chdata);
	}
	/**
	 * 
	 * @param pac
	 */
	// ------------------------------------------
	// UDPパケットからデータを追加
	public synchronized void addChData(DatagramPacket pac)
	{
		PacketCounter++;
		DataBase.addChData(pac);
		ChannelNumChangeFlag=true;
	}
	
	// ------------------------------------------
	// ファイルからデータを追加する
	public void addChData(String file){
		DataBase.addChData(file);
	}
	// ------------------------------------------
	// ファイルツリーセットからデータを追加する
	public void addChData(TreeSet<String> ts){
		DataBase.addChDataTh(ts);
	}
	
	// ------------------------------
	// ポップアップメニューをＡＤＤする
	private JMenuItem addPopupMenuItem(JPopupMenu popup,String name, ActionListener al) {
		JMenuItem item = new JMenuItem(name);
		item.addActionListener(al);
		popup.add(item);
		return item;
	}
	private JMenuItem addPopupMenuItem(JMenu popup,String name, ActionListener al) {
		JMenuItem item = new JMenuItem(name);
		item.addActionListener(al);
		popup.add(item);
		return item;
	}
	private JMenuItem addPopupMenuItem(JPopupMenu popup,String name, ActionListener al,ImageIcon icon) {
		JMenuItem item = addPopupMenuItem(popup,name,al);//@
		item.setIcon(icon);
		return item;
	}

	// ------------------------------------------
	// ＵＤＰデータからデータを追加
	public  void addUdpChData(byte[] pacdata){
		PacketCounter++;
		DataBase.addUdpChData(pacdata);
		ChannelNumChangeFlag=true;

	}
 	
	public void afterAddBlockData(byte[] chdata){
		afterAddChData();
	}
	private void afterAddChData(){
		if(GVDispTimeStart.year()==0){
			GVDispTimeStart = new wintime(DataBase.OldestTime);
			int span = DataBase.LatestTime.getValueS()-GVDispTimeStart.getValueS();
			if(span > 60*60*24) span = 60*60*24;
			//if(span<1) span=1;
			if(span < 60*3) span = 60*3;
			setDispTimeRange_ms(span*1000);// 初期値
		}
		else{
			int stm = GVDispTimeStart.getValueS();			// 表示開始時間
			long etm = stm + (GVDispTimeRange_ms+999)/1000; 		// 表示終了時間
			int ctm = DataBase.LatestTime.getValueS();		// 表示開始時間
			if(AutoDisplay){
				if(ctm > etm){
					GVDispTimeStart.inc_sec((int)(ctm-etm));
					//DispTimeStart = new wintime(DataBase.LatestTime);
					//DispTimeStart.dec_sec((int)DispTimeRange_ms/1000);
					onDispTimeChange();
				}
				//System.out.println(String.format("LatestTime=%d,DispLatest=%d,OldStart=%d,NewStart=%d", ctm,etm,stm,DispTimeStart.getValueS()));
			}
		}
		//setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
	}
	
	//---------------------------------------------------------------------------------
	// 時間表示をアレンジする
	private HashMap<Integer,wintime> arrangeTimeDisplay(TreeMap<Integer,wintime> map,int min){
		HashMap<Integer,wintime> ret = new HashMap<Integer,wintime>();
		int fk = map.firstKey();
		int feva = (fk >> 16)&0xffff;
		TreeMap<Integer,wintime> tmp=null;
		while(true){
			tmp =  arrangeTimeDisplayFindEva(map,feva);
			if(tmp==null) break;
			int sz = tmp.size();
			min -= sz;
			if(min<0) break;
			ret.putAll(tmp);	
			feva++;
		}
		return ret;
	}
	//---------------------------------------------------------------------------------
	// 指定した評価レベルのマップを選び出す
	private TreeMap<Integer,wintime> arrangeTimeDisplayFindEva(TreeMap<Integer,wintime> map,int feva){
		TreeMap<Integer,wintime> ret = new TreeMap<Integer,wintime>();
		
		Iterator<Integer> ite = map.keySet().iterator();
		while(ite.hasNext()){
			int key = ite.next();
			int xpos = key & 0xffff;
			int eva = (key >> 16)&0xffff;
			wintime wt = map.get(key);
			if(feva==eva){
				ret.put(xpos,wt);
			}
		}
		return ret;
	}
	public boolean beforeAddBlockData(byte[] chdata){
		beforeAddChData();
		return true;
	}
	private void beforeAddChData(){
		//setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)));
	}
	/**
	 * 描画モードを決める
	 * @param xmax
	 * x方向の最大値（画面pixcel)
	 * @param xbound
	 * x方向のスタート座標（画面pixcel)
	 * @param samplerate
	 * １秒あたりのサンプルポイント数
	 * @param numsec
	 * 描きたい時間
	 * @return
	 * 0=ドット毎全て描く、1=１秒に１ドット描く 、2=１分に1ドット描く
	 */
	private int calcDrawMode(double xmax, int xbound, int samplerate,long numsec) {
//		int xwidth = (int)xmax - xbound;
//		float datapoint = numsec*samplerate;
//		if(datapoint==0) return 9;// たぶんデータがない
//		// 描画間引きのために
//		int drawmode=0;
//		if(numsec<xwidth*1){
//			drawmode = 0;
//		}
//		else{
//			if((numsec/60)<xwidth*1){
//				drawmode=1;
//			}
//			else{
//				drawmode=2;
//			}
//		}
//		return drawmode;
		int xwidth = (int)xmax - xbound;
		long datapoint = numsec*samplerate;
		if(datapoint==0) return 0;// たぶんデータがない
		// 描画間引きのために
		int drawmode=0;
		if(numsec<xwidth*1){
			drawmode = 0;
		}
		else{
			if((numsec/60)<xwidth*1){
				drawmode=1;
			}
			else{
				drawmode=2;
			}
		}
		return drawmode;
	}
	private void clearChannel(){
		DataBase.clearChannel();
	}
	//	private TreeMap<Integer,ChannelDataCollection> getDataTreeMap(){
//		return DataBase.getData();
//	}
	private void clearData(){
		DataBase.clearData();
	}

	private Object DataBaseLock(){
		return DataBase.Lock;
	}
	// 
	// ------------------------------------------
	// データを古いもの(==OldestTime)から削除する
	private int deleteOldestData(){
		// すべてのｃｈからＯｌｄｅｓｔＴｉｍｅ以前のデータを削除する
		//System.out.println("delete-----------------");
		int deletedByte=0;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch =ite.next();
				ChannelDataCollection dcol = getChannelDataCollection(ch);
			    TreeMap<Integer,ChannelData> cdlist = dcol.getChannelData();
				if(!cdlist.isEmpty()){
				    int fk = cdlist.firstKey();
					ChannelData fd = cdlist.get(fk);
					if(fd.getTime().getValueS() <= DataBase.OldestTime.getValueS()){
						deletedByte += fd.getData().length;
						cdlist.remove(fk);
						//System.out.println("remove");
						//System.out.println("purge");
					}
				}
			}
		}
		 //すべてのｃｈのうちもっとも古いものをOldestTimeにセットする
		TreeMap<Integer,Integer> tmmap = new TreeMap<Integer,Integer>();
		synchronized(DataBaseLock()){
			Iterator<Integer> ite2 = iteratorChannel();
			while(ite2.hasNext()){
				int ch =ite2.next();
				ChannelDataCollection dcol = getChannelDataCollection(ch);
			    TreeMap<Integer,ChannelData> cdlist = dcol.getChannelData();
			    if(!cdlist.isEmpty()){
				    int fk = cdlist.firstKey();
					ChannelData fd = cdlist.get(fk);
					tmmap.put(fd.getTime().getValueS(),ch);
				}
			}
		}
		if(!tmmap.isEmpty()){
			Integer oldch = tmmap.firstEntry().getValue();
			TreeMap<Integer,ChannelData> cdlist2 = getChannelDataCollection(oldch).getChannelData(); 
			int key = cdlist2.firstKey();
			ChannelData fd2 = cdlist2.get(key);
			DataBase.OldestTime = new wintime(fd2.getTime());
		}
		return deletedByte*4;
	}
	private int deleteOldestDataNEW(int delsec){
		// すべてのｃｈから
		//System.out.println("delete-----------------");
		int deletedByte=0;
		synchronized(DataBaseLock()){
//			int oldData = DataBase.OldestTime.getValueS();
//			int newData = DataBase.LatestTime.getValueS();
			// 表示している時間を求めて
			long oldDisp = GVDispTimeStart.getValueS();
			long newDisp = oldDisp ;
			long oldData = DataBase.OldestTime.getValueS();
			long newData = DataBase.LatestTime.getValueS();
			// どちら側からデータを削除するか決める
			boolean fromold=true;	// 最古から削除
			if(oldDisp<=oldData && newData <= newDisp) return 0; // 削除出来ない
			if(oldDisp<=oldData && newDisp <= newData){
				fromold=false; // 最新から削除
			}
			else if(oldData<=oldDisp && newDisp <= newData){ // 表示エリアがデータエリアの内部の場合
				if(oldDisp-oldData < newData-newDisp){ // 表示エリアからより離れているほうを削除の対象にする
					fromold=false;		
				}
				else fromold = true;
			}
			// データをpurgeする
		//	deletedByte = DataBase.purgeData(delsec,fromold);
		}
		//System.out.println(String.format("purge %,dKB",deletedByte*4/1024));
		return deletedByte*4;
	}
	// -------------------------------------
	// Y軸の表示単位をdigit / 物理単位切り替える
	private void digitPhysicalQuantity(){
		if(ChannelPropDialog != null){
			if(ChannelPropDialog.isShowing()){
				ChannelPropDialog.dispose();
			}
		}
		//ChannelPropDialog = new DialogChannelProperty((Frame)(this.getParent().getParent().getParent()));
		ChannelPropDialog = new DialogSingleChannelAmplitudeProperty(getParentFrame());
		ChannelPropDialog.setParent(this);

		// Point p = getParent().getParent().getParent().getLocation();
		Point p = getParentPoint();
		ChannelPropDialog.setLocation(100+p.x+CurrentPopupPosition.x, 100+p.y+CurrentPopupPosition.y);
		//ChannelPropDialog.setLocation(CurrentPopupPosition.x, CurrentPopupPosition.y);
		ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
		if(pro != null){
		
			ChannelPropDialog.ChID = pro.getChannel();
			switch(pro.unitType){
			case ChannelDataDisplayProperty.UNIT_TYPE_CONV:
				ChannelPropDialog.ConvMode = ChannelPropDialog.UnitModePU;
				break;
			case ChannelDataDisplayProperty.UNIT_TYPE_DIGIT:
			default:
				ChannelPropDialog.ConvMode = ChannelPropDialog.UnitModeDigit;
			}
			ChannelPropDialog.UnitText = pro.unitString;
			ChannelPropDialog.Factor = pro.unitFactor;
			ChannelPropDialog.FullScale = pro.FullScale;
			ChannelPropDialog.setparam();
			ChannelPropDialog.setVisible(true);
		}	
		repaintAll();
	}
	// ------------------------------
	// 自動スケール
	public void doAutoScale() {
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			if(pro != null){
				pro.mode = ChannelDataDisplayProperty.MODE_AUTO;
			}
		}
		repaintAll();
	}
	// ------------------------------------------
	//　CHANNEL表示を１つ減らす
	public void doChannelDec(){
		int disp2 = getLastDisplayChannel();
		if(disp2 != -1){
			ChannelDataDisplayProperty pro = getChannelInfo(disp2);
			 pro.hide = 1;
			 ManualYScale.put(disp2, pro);
			 ChConfig.setData(disp2, pro);
			ChannelNumChangeFlag=true;
		}
		repaintAll();
	}
	// ------------------------------------------
	// 一つ下のCHANNELを表示する
	public void doChannelDown(){
		showChannelDown(1);
		repaintAll();
	}
	//　CHANNEL表示を１つ増やす
	public int doChannelInc(){
		int ret = 0;
//		int disp2 = getLastDisplayChannel();
//		int disp3 = -1;
//		if(disp2 != -1) disp3=getNextChannel(disp2);
//		else {
//			//		if(!getChannel().isEmpty()) disp3= getChannel().first();
//			if(!isEmptyChannel()) disp3= firstChannel();
//		}
//		if(disp3 != -1){
//			ChannelDataDisplayProperty pro = getChannelInfo(disp3);
//			 pro.hide = 0;
//			 ManualYScale.put(disp3, pro);
//			 ChConfig.setData(disp3, pro);
//			ChannelNumChangeFlag=true;
//			ret++;
//		}
		int disp = getLastUnDisplayChannel();
		if(disp<=0){
			return ret;
		}
		ChannelDataDisplayProperty pro = getChannelInfo(disp);
		pro.hide = 0;
		ManualYScale.put(disp, pro);
		ChConfig.setData(disp, pro);
		ChannelNumChangeFlag=true;
		ret++;
		
		repaintAll();
		return ret;
	}
	// -------------------------------------------------------------------
	// ------------------------------------------
	public void doChannelMax(){
		while(doChannelInc()>0){
			
		}
	}
	// ------------------------------------------
	// 一つ下のCHANNELを表示する
	public void doChannelPageDown(){
		showChannelDown(GraphNum);
		repaintAll();
	}
	// ------------------------------------------
	// 一つ上のCHANNELを表示する
	public void doChannelPageUp(){
		showChannelUp(GraphNum);
		repaintAll();
	}
	// ------------------------------------------
	// 一つ上のCHANNELを表示する
	public void doChannelUp(){
		showChannelUp(1);
		repaintAll();
	}
	// ------------------------------
	// 最新の波形を表示 
//	private void xLatestView(){
//		wintime tm = new wintime(LatestTime);
//		tm.dec_msec(DispTimeRange_ms);
//		DispTimeStart = tm;
//		repaintAll();
//	}
	// ------------------------------
	// Clip board に平均値をコピーする 
	private void doCopyClipBoard(){
		String mes0 = "ch= \t";
		String mes = "m= \t";
		String mes2 = "u= \t";
		
		//	for(int i=0;i<getChannel().size();i++){
		for(int i=0;i<DisplayChannelNumber;i++){
			int dispindex = DisplayChannel[i];
			mes0 += String.format(" %04x \t",DataChannel[dispindex]);
			mes += String.format(" %f \t", Datamean[dispindex]);
			mes2 += String.format(" %f \t", Datavariant[dispindex]);
		}
		mes = mes0 + "\n" + mes + "\n" +  mes2;
		Toolkit kit = Toolkit.getDefaultToolkit();
		Clipboard clip = kit.getSystemClipboard();
		StringSelection ss = new StringSelection(mes);
		clip.setContents(ss, ss);
		// 
		mes = String.format("Copy %d channel(s)", DisplayChannelNumber) + "\n" + mes;
		JOptionPane.showConfirmDialog(this, mes,"Copy average value at marker", JOptionPane.CLOSED_OPTION, JOptionPane.INFORMATION_MESSAGE);

	}
	/**
	 * 
	 */
	private void doMarkerClipBoard(){
		
		
		wintime[] cwm = getDialogTimeMarker().getWinTime();
		if(cwm.length<=0){
			JOptionPane.showConfirmDialog(this, "No Time marker.\nTo add a time marker,Ctrl + left click on time chart area.", "Copy average on time marker", JOptionPane.CLOSED_OPTION, JOptionPane.WARNING_MESSAGE);
			return;
		}
		// チャンネルリストを求めて
		ArrayList<Integer> chlist =  getDisplayChannelList();
		String mes = new String();
		mes += String.format("Marker  \t time \t ");
		Iterator<Integer> itechdisp=chlist.iterator();
		while(itechdisp.hasNext()){
			int ch=itechdisp.next();
			mes += String.format("%04x \t ", ch);
		}
		mes += " \n";
		int wtlen=3;// マーカから何秒分の平均をとるか。
		String wtlenstr = JOptionPane.showInputDialog(this, "Time range from marker [s]",(Object)wtlen);
		
		try{
			wtlen = Integer.valueOf(wtlenstr);
		}catch(NumberFormatException e){
			wtlen = 3;
		}
		int[] channel = getDialogTimeMarker().getMarkerIndex();
		for(int i=0;i<cwm.length;i++){
			mes += String.format("Marker %d \t ",channel[i]);
			mes += cwm[i].fmt() + " \t ";
			Iterator<Integer> itech=chlist.iterator();
			while(itech.hasNext()){
				int ch=itech.next();
				ChannelOutlineDataCollection dcol = DataBase.getChannelDataCollection1(ch);	
				if(dcol==null){
					continue;
				}
				wintime wt = new wintime(cwm[i]);
				double mean = 0.0;
				for(int j=0;j<wtlen;j++,wt.inc_sec()){
					ChannelOutlineData od = dcol.getChannelData(wt);	
					if(od!=null){
						mean += od.getMean();
					}
				}
				mean /= wtlen;
				mes += String.format("%.3f \t ",mean);
			}
			mes += "\n";
		}
		Toolkit kit = Toolkit.getDefaultToolkit();
		Clipboard clip = kit.getSystemClipboard();
		StringSelection ss = new StringSelection(mes);
		clip.setContents(ss, ss);
		//
		mes = String.format("Marker time from 0 sec to %d sec", wtlen) + "\n" + mes;
		JOptionPane.showConfirmDialog(this, mes,"Copy average value at marker", JOptionPane.CLOSED_OPTION, JOptionPane.INFORMATION_MESSAGE);

	}
	/**
	 * 時刻セットダイアログ
	 */
	public void doTimeSetDialog(){
		getDialogTimeSetting().setTime(GVDispTimeStart);
		getDialogTimeSetting().setLocation(this.getLocationOnScreen());
		getDialogTimeSetting().setVisible(true);
	}
	// 任意の表示開始時間をセットする
	public void setDispTimeStart(wintime wt){
		GVDispTimeStart = new wintime(wt);
		onDispTimeChange();
		repaintAll();
	}
	// 任意の表示開始時間をセットする
	public void setDispTimeCenter(wintime wt){
		GVDispTimeStart = new wintime(wt);
		GVDispTimeStart.dec_msec(GVDispTimeRange_ms/2);
		onDispTimeChange();
		repaintAll();
	}
	/**
	 * 表示時間を戻す
	 */
	public void doDecTime(){
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			int delta = (int)(GVDispTimeRange_ms / 5);
			GVDispTimeStart.dec_msec(delta);
			onDispTimeChange();
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
//			DrumTimeTrancate=false;
			GVDispTimeStart.dec_sec(DrumTimeSpanSec);
			onDispTimeChange();
		}
		repaintAll();
	}
	/**
	 * 表示時間を進める
	 */
	public void doIncTime(){
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			int delta = (int)(GVDispTimeRange_ms / 5);
			GVDispTimeStart.inc_msec(delta);
			onDispTimeChange();
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
//			DrumTimeTrancate=false;
			GVDispTimeStart.inc_sec(DrumTimeSpanSec);
			onDispTimeChange();
		}
		repaintAll();
	}
	/**
	 * 表示時間を1ページ戻す
	 */
	public void doPageDecTime(){
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			int delta = (int)GVDispTimeRange_ms;
			GVDispTimeStart.dec_msec(delta);
			onDispTimeChange();
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
//			DrumTimeTrancate=true;
			GVDispTimeStart.dec_sec(DrumTimeSpanSec*DrumTimeRowCount);
			onDispTimeChange();
		}
		repaintAll();
	}
	/**
	 * 表示時間を1ページ進める
	 */
	public void doPageIncTime(){
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			int delta = (int)GVDispTimeRange_ms;
			GVDispTimeStart.inc_msec(delta);
			onDispTimeChange();
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
//			DrumTimeTrancate=true;
			GVDispTimeStart.inc_sec(DrumTimeSpanSec*DrumTimeRowCount);
			onDispTimeChange();
		}
		repaintAll();
	}

	

	// ------------------------------
	// 全部物理換算
	public void doDigitPhysicalAll(boolean phy) {
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			// 表示されているCHのみを対象にする。
			if(!pro.isDisplay()) continue; 
			if(pro != null){
				if(phy) 	pro.unitType = ChannelDataDisplayProperty.UNIT_TYPE_CONV;
				else  		pro.unitType = ChannelDataDisplayProperty.UNIT_TYPE_DIGIT;
			}
		}
		repaintAll();
	}
	
	// ------------------------------
	// 全部自動オフセット
	public void doScaleOffsetAll(boolean on) {
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			// 表示されているCHのみを対象にする。
			if(!pro.isDisplay()) continue; 
			if(pro != null){
				if(on){
					pro.offset = (int)pro.getCenterValue();
					pro.offsetEnable=true;
				}
				else{
					pro.offset=0;
					pro.offsetEnable=false;
				}
			}
		}
		repaintAll();
	}
	/**
	 * 表示されているＣＨがすべてＤＩＧITスケールかどうかを調べる
	 * @return
	 */
	private boolean isAllDigit(){
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			// 表示されているCHのみを対象にする。
			if(!pro.isDisplay()) continue; 
			if(pro != null){
				if(pro.unitType != ChannelDataDisplayProperty.UNIT_TYPE_DIGIT){
					return false;
				}
			}
		}
		return true;
	}
	/**
	 * 物理単位がunitなｃｈの集合を返す
	 * @param unit
	 * @return
	 */
	private LinkedList<Integer> getChannel(String unit){
		LinkedList<Integer> ret = new LinkedList<Integer>();
		
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			if(!pro.isDisplay()) continue; 
			if(pro != null){
				String ustr = pro.unitString;
				if(pro.unitType == ChannelDataDisplayProperty.UNIT_TYPE_DIGIT){
					ustr = "DIGIT";
				}
				if(ustr.compareTo(unit)==0){
					ret.add(pro.getChannel());
				}
			}
		}
		return ret;
	}
	/**
	 * 物理チャンネルごとのｃｈリストを返す。
	 * @return
	 */
	private HashMap<String,LinkedList<Integer>> getGroupUnit(){
		HashMap<String,LinkedList<Integer>> ret = new HashMap<String,LinkedList<Integer>>();
		
		// どんな種類の単位があるかを調べる。
		HashSet<String> unit = new HashSet<String>();
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			if(!pro.isDisplay()) continue; 
			if(pro != null){
				if(pro.unitType == ChannelDataDisplayProperty.UNIT_TYPE_DIGIT){
					unit.add("DIGIT");
				}
				else{
					unit.add(pro.unitString);
				}
			}
		}
		Iterator<String> ite2  = unit.iterator();
		while(ite2.hasNext()){
			String u  = ite2.next();
			LinkedList<Integer> chl = getChannel(u);
			ret.put(u,chl);
		}
		return ret;
	}
	/**
	 * chリストをsigleScaleする.
	 * @param ch
	 */
	private void doSingleScale(LinkedList<Integer> ch){
		// 最大値を求めて
		Iterator<Integer> ite = ch.iterator();
		double maxspan = 0.0;
		while(ite.hasNext()){
			int c = ite.next();
			ChannelDataDisplayProperty pro = getChannelInfo(c);
			double d = (pro.getMaxDisp() - pro.getMinDisp())*pro.unitFactor;
			if(maxspan<d) maxspan=d;
		}
		// その値にスケールを合わせる
		ite = ch.iterator();
		while(ite.hasNext()){
			int c = ite.next();
			ChannelDataDisplayProperty pro = getChannelInfo(c);
			long scale = (long)(maxspan /pro.unitFactor/2.0);
			pro.mode = ChannelDataDisplayProperty.MODE_FIX;
			long cen = pro.getCenterValue();
			long max = cen + scale;
			long min = cen - scale;
			pro.setMaxMin(max,min);
		}
	}
	/**
	 * シングルスケール
	 */
	public void doSingleScale() {
		
		if(isAllDigit()){  // すべての表示ＣＨがＤＩＧＩＴ表示なら従来どおり
			Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
			Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
			while(ite.hasNext()){
				Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
				ChannelDataDisplayProperty pro = ent.getValue();
				// 表示されているCHのみを対象にする。
				if(!pro.isDisplay()) continue; 
				if(pro != null){
					pro.mode = ChannelDataDisplayProperty.MODE_FIT_SCALE;
				}
			}
		}
		else{
		// 物理単位毎にグループ分けして、グループ毎にSingleScaleを実行する
			HashMap<String,LinkedList<Integer>> u = getGroupUnit();
			Set<String> s  = u.keySet();
			Iterator<String> ite3  =s.iterator();
			while(ite3.hasNext()){
				String sss = ite3.next();
				LinkedList<Integer> lst = u.get(sss);
				doSingleScale(lst);
			}
		}
		repaintAll();
	}
	
	/**
	 * 同じ単位のｃｈを、単一の振幅に統一
	 * @param scale
	 */
	public void doSingleScale(long scale) {
		doSingleScale(scale,null);
	}
	/**
	 * 同じ単位のｃｈを、単一の振幅に統一
	 * @param scale　常にdigit
	 * @param p 参照するＣＨプロパティ（物理単位表示なら、同じ物理単位のＣＨを対象にする） </BR>
	 *           null はDigitを仮定する。
	 */
	public void doSingleScale(long span,ChannelDataDisplayProperty p) {
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		boolean physical = false;
		if(p!=null){
			if(p.unitType == ChannelDataDisplayProperty.UNIT_TYPE_CONV){
				physical = true;
			}
		}
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			if(pro != null){
				// 表示されているCHのみを対象にする。
				if(pro.isDisplay()){
					if(!physical || (physical && p.unitString.compareTo(pro.unitString)==0 && pro.unitType ==ChannelDataDisplayProperty.UNIT_TYPE_CONV)){
						long scale;
						if(physical){
							scale = (long)(span / pro.unitFactor *p.unitFactor/2.0);
						}
						else{
							scale = span/2;
						}
						pro.mode = ChannelDataDisplayProperty.MODE_FIX;
						long cen = pro.getCenterValue();
						long max = cen + scale;
						long min = cen - scale;
						pro.setMaxMin(max,min);
					}
				}
			}
		}
		repaintAll();
	}
	/**
	 * 同じ単位のｃｈを、単一の振幅に統一
	 * @param scale　常に物理単位
	 * @param unit  物理単位 </BR>
	 */
	public void doSingleScale(String unit,double scale) {
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		boolean physical = true;
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			if(pro != null){
				// 表示されているCHのみを対象にする。
				if(pro.isDisplay()){
					if(!physical || (physical && unit.compareTo(pro.unitString)==0 && pro.unitType ==ChannelDataDisplayProperty.UNIT_TYPE_CONV)){
						long scl = (long)(scale / pro.unitFactor / 2.0);
						pro.mode = ChannelDataDisplayProperty.MODE_FIX;
						long cen = pro.getCenterValue();
						long max = cen + scl;
						long min = cen - scl;
						pro.setMaxMin(max,min);
					}
				}
			}
		}
		repaintAll();
	}
	/**
	 * 単一のセンターに統一
	 * @param scale
	 */
	public void doSingleCenter(long cen,ChannelDataDisplayProperty p) {
		Set<Entry<Integer,ChannelDataDisplayProperty>> s = ManualYScale.entrySet();
		Iterator<Entry<Integer, ChannelDataDisplayProperty>> ite = s.iterator();
		boolean physical = false;
		if(p!=null){
			if(p.unitType == ChannelDataDisplayProperty.UNIT_TYPE_CONV){
				physical = true;
			}
		}
		while(ite.hasNext()){
			Entry<Integer, ChannelDataDisplayProperty> ent = ite.next();
			ChannelDataDisplayProperty pro = ent.getValue();
			if(pro != null){
				// 表示されているCHのみを対象にする。
				if(pro.isDisplay()){
					if(!physical || (physical && p.unitString.compareTo(pro.unitString)==0 && pro.unitType ==ChannelDataDisplayProperty.UNIT_TYPE_CONV)){
						long center = cen;
						if(physical){
							center = (long)(cen / pro.unitFactor *p.unitFactor);
						}
						pro.setCenterValue(center);
					}
				}
			}
		}
		repaintAll();
	}
	/**
	 * 時刻を自動セットする
	 */
	public void doTimeAuto(){
		GVDispTimeStart = new wintime(DataBase.OldestTime);
		onDispTimeChange();
		repaintAll();
	}
	/**
	 * 表示開始時間をセットする。（全体を０～１で正規化）
	 * @param pos　
	 */
	private void doSetTime(double pos){
	
		wintime st=null;
		// 保持データの最古と最新　１秒
		int data1 = DataBase.OldestTime.getValueS();
		int data2 = DataBase.LatestTime.getValueS();
		// 表示している領域
		int disp1 = GVDispTimeStart.getValueS();
		int disp2 = disp1+(int)(GVDispTimeRange_ms/1000L);
		// 表示領域と保持している領域の全体を分母にする
		int start;
		if(data1<disp1) start=data1;
		else			start=disp1;
		int end;
		if(disp2>data2) end=disp2;
		else			end=data2;
		int refspan = end-start;
		if(refspan<=0) refspan=1;
		if(data1<disp1) { start=data1; st=new wintime(DataBase.OldestTime);}
		else			{ start=disp1; st=new wintime(GVDispTimeStart);}
		st.inc_msec((long)(pos*refspan*1000));
		st.dec_msec((int)(GVDispTimeRange_ms/2));
		GVDispTimeStart=st;
		onDispTimeChange();
		repaintAll();
	}
	/**
	 * スパンをセットする
	 * @param span
	 * 　　ｍｓで指定
	 */
	public void doSpan(int span){
		if(GraphType == GRAPHIC_TYPE_NORMAL || GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP) {
			if(span < 20) span = 20;
			float amp = (float)span/(float)GVDispTimeRange_ms;
			
			//System.out.println(DispTimeStart.fmt());
			wintime tm = new wintime(GVDispTimeStart);
			float pos;
			if(CursorDisplay!=0){
				pos = (float)(CurrentMousePositon.x - AreaGraph.x)/(float)AreaGraph.width;
			}
			else{
				pos = 0.5f;
			}
			if(amp < 1){
				long msec = (long)((float)GVDispTimeRange_ms*pos*(1-amp));
				//System.out.println(String.format("inc= %d msec",msec));
				tm.inc_msec(msec);	// Center Time
			}
			else{
				long msec = (long)((float)GVDispTimeRange_ms*pos*(amp-1));
				//System.out.println(String.format("dec= %d msec",msec));
				tm.dec_msec(msec);	// Center Time
			}
			GVDispTimeStart=tm;		// set start time
			//System.out.println(DispTimeStart.fmt());
	    	setDispTimeRange_ms(span);	// set new range;
			onDispTimeChange();
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
			int min = span/(60*1000);
			if(min<3)				DrumTimeSpanCode=DrumTimeSpan1Minute;
			else if(min < 10)		DrumTimeSpanCode=DrumTimeSpan3Minute;
			else if(min < 30)		DrumTimeSpanCode=DrumTimeSpan10Minute;
			else if(min < 60)		DrumTimeSpanCode=DrumTimeSpan30Minute;
			else if(min < 60*3)		DrumTimeSpanCode=DrumTimeSpan1Hour;
			else if(min < 60*12)	DrumTimeSpanCode=DrumTimeSpan3Hour;
			else if(min < 60*24)	DrumTimeSpanCode=DrumTimeSpan12Hour;
			else if(min < 60*24*3)	DrumTimeSpanCode=DrumTimeSpan1Day;
			else if(min < 60*24*10)	DrumTimeSpanCode=DrumTimeSpan3Day;
			else 					DrumTimeSpanCode=DrumTimeSpan10Day;
			drumSetSpan(DrumTimeSpanCode);
			repaintAll();
		}
	}
	/**
	 * 時間軸を拡大　（ズームイン）
	 */
	public void doZoomIn(){
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			if(GVDispTimeRange_ms > 2) doSpan((int)(GVDispTimeRange_ms/2));
			else  doSpan(1);
			repaintAll();
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
			if(DrumTimeSpanCode > 0){
				DrumTimeSpanCode--;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
		}
	}
	/**
	 * 時間軸を縮小　（ズームアウト）
	 */
	public void doZoomOut(){
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			if(GVDispTimeRange_ms>=Integer.MAX_VALUE) return;
			long tmp = GVDispTimeRange_ms;
			tmp *= 2L;
			if(tmp < Integer.MAX_VALUE/2) doSpan((int)(GVDispTimeRange_ms*2));
			else doSpan(Integer.MAX_VALUE/2);
			repaintAll();
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
			if(DrumTimeSpanCode < DrumTimeSpanMax){
				DrumTimeSpanCode++;
				drumSetSpan(DrumTimeSpanCode);
				repaintAll();
			}
		}
	}
	//---------------------------------------
	// ドロップターゲットリスナー
	@Override
	public void dragEnter(DropTargetDragEvent arg0) {
		//System.out.println("dragEnter");
	}
	@Override
	public void dragExit(DropTargetEvent arg0) {
		//System.out.println("dragExit");
	}
	@Override
	public void dragOver(DropTargetDragEvent arg0) {
		//System.out.println("dragOver");
	}
	/**
	 * chの統計情報(平均、標準偏差）などを描く
	 * @param gd2
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param space
	 * @param chnum
	 * @param mean
	 * @param variance
	 * @param cursorData
	 * @param samplerate
	 */
	private void drawChannelData(Graphics2D gd2, int x1,int y1,int x2, int y2,int space,int chnum,double mean,double variance,int cursorData,int samplerate) {
		//double ymax = y2-y1-space*2;
		//Graphics2D gd2 = (Graphics2D)gd.create(x1,y1+space,x2,y2-space);

		int charhightorg = (y2-y1-2)/4;
		//float fp = (float)(charhight*72/96.0f);
		float fp = (float)getFontPoint(charhightorg);
		
		if(fp>12){
			fp = 12f;
		}
		if(fp<8) return; // 小さすぎたら書かない
		Color orgcol = gd2.getColor();
		gd2.setColor(ColorTextInfo);
		
		//charhight = (int)(fp * 96 / 72);
		int charhight = (int)getFontHeight((int)fp);
		Font fon2 = gd2.getFont().deriveFont(fp);
		gd2.setFont(fon2);
	
		ChannelDataDisplayProperty pro = getChannelInfo(chnum);
		
		// 平均値を表示する（表示秒間）
		//String meanchar = String.format("μ=%,5d", mean);
		String meanchar = "m=" + pro.convUnit((double)mean,true);
		gd2.drawString(meanchar,x1+10,y1+space + (int)(charhight)+2);
		// 標準偏差を表示する（表示秒間）
		//String variencechar = String.format("σ=%5.3f", variance);
		String variencechar;
		if(ConvSigmaSNR){
			double fs = (double)pro.FullScale;
			double va = (double)variance;
			double sn = 20.0*Math.log10(va/fs);
			variencechar = String.format("SNR=%6.2f dB", sn);
		}
		else{
			variencechar = "u=" + pro.convUnit((double)variance,true);
		}
		gd2.drawString(variencechar,x1+10,y1+space+(int)(charhight*2)+2);
		// カーソル点のデータを表示
		//String cursorDatachar = String.format("@=%,5d", cursorData);
		String cursorDatachar = "@=" + pro.convUnit((double)cursorData,true);
		gd2.drawString(cursorDatachar,x1+10,y1+space+(int)(charhight*3)+2);
		String smplrate = String.format("fs=%d", samplerate);
		gd2.drawString(smplrate,x1+10,y1+space+(int)(charhight*4)+2);
		
		gd2.setColor(orgcol);
		
	}
	/**
	 * ＣＨ番号（ＣＨＩＤ）を描画する
	 * @param gd2
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param space
	 * @param chnum
	 * @param fp
	 * @param pro
	 * @param col
	 */
	private void drawChannelNumber(Graphics2D gd2, int x1,int y1,int x2, int y2,int space,int chnum,float fp,ChannelDataDisplayProperty pro,Color col) {
		
		if(!DispChannelNumber) return;

		//double ymax = y2-y1-space*2;
		//Graphics2D gd2 = (Graphics2D)gd.create(x1,y1+space,x2,y2-space);
		// ＣＨ番号を描画
		Color orgcol = gd2.getColor();
		gd2.setColor(ColorTextInfo);
		String ch = "";
		
		if(ShowChannelNumber) 	ch += String.format("[%04X]",chnum);
		if(ShowStation)  		ch += String.format(" %s",pro.getStation());
		if(ShowComponent) 		ch += String.format(" %s",pro.getComponent());
		
		
		if(fp<12) fp=12; // １２pointよりちいさくしない
		if(chnum == CurrentMouseChannel){
			if(GraphType == GRAPHIC_TYPE_NORMAL && !IsPrint){
				//ノーマルモードでは印刷時に＊マークを描かない	
			}
			else{
				// ＯｖｅｒＬａｐｐｅｄモードのときは、ＹスケールがどのＣＨのスケールかわかるように印刷時でも描画する
				ch += " *";
			}
		}
		
		//Font fon2 = gd2.getFont().deriveFont(fp);
		Font fonorg = gd2.getFont();
		Font fon2 = new Font("Monospaced",Font.PLAIN,(int)fp);
		gd2.setFont(fon2);
		int fonhight = getFontHeight((int)fp);
		gd2.drawString(ch,x1+2+10,y1+space + fonhight);
		if(col!=null){
			gd2.setColor(col);
			gd2.fillRect(x1+2, y1+space+5,8 , fonhight-2);
		}
		gd2.setFont(fonorg);
		gd2.setColor(orgcol);
	}
	/**
	 * タイムマーカーを描画する
	 * @param gd2
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 */
	private void drawMarker(Graphics2D gd2,int x1,int y1,int x2,int y2){
		if(TmMarker.size()==0) return; 
		Color org = gd2.getColor();
		Stroke strokeorg = gd2.getStroke();
		gd2.setStroke(Dashed1);
		Iterator<TimeMarker> ite = TmMarker.iterator();
		while(ite.hasNext()){
			TimeMarker tm = ite.next();
			if(tm.Number==0){	// 0番はリファレンスマーカー
				gd2.setColor(ColorMarker0);
			}
			else{
				gd2.setColor(ColorMarker1);
			}
			tm.Xpos += x1;
			gd2.drawLine(tm.Xpos,y1,tm.Xpos,y2-4);
		}
		gd2.setStroke(strokeorg);
		gd2.setColor(org);
	}
	
	/**
	 * カレントカーソル時間を計算する。
	 */
	private void calcCurrentCursorTime(){
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			int xpos = CurrentMousePositon.x-AreaGraph.x;
			int xspan = AreaGraph.width;
			CurrentMouseTime = x2time(xpos,xspan,GVDispTimeStart,GVDispTimeRange_ms);
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
			wintime wt = geTimeFromPointOnDrumMode(CurrentMousePositon);
			if(wt == null){
				CursorDisplay=0;
			}
			else {
				CursorDisplay=1;
				CurrentMouseTime =wt;
				drawMisc();
			}
		}
				
	}
	/**
	 * 座標から時刻を求める
	 * @param pt 
	 *  Point マウスの座標（View内）
	 * @return
	 */
	private wintime geTimeFromPointOnDrumMode(Point pt){
		int xpos = pt.x;
		int ypos = pt.y;
		for(int i =0;i<DrumRect.size();i++){
			Rectangle r = DrumRect.get(i);
			if(r.contains(xpos,ypos)){
				if(xpos-r.x < DrumGraphXbound || xpos-r.x > r.width  - DrumGraphXMargin){
					return null;
				}
				if(ypos-r.y < DrumGraphYTopMargin || ypos-r.y > r.height - DrumGraphYbound){
					return null;
				}
				int x = xpos - r.x - DrumGraphXbound;	//
				int w = r.width - DrumGraphXbound - DrumGraphXMargin;		// 
				int xsec = (int)((float)x/(float)w * DrumTimeSpanSec); // x方向の時刻
				int y = ypos - r.y - DrumGraphYTopMargin;		
				int n = (int) (y / ((float)(r.height-DrumGraphYbound-DrumGraphYTopMargin) / (float)DrumTimeRowCount)); // 何番目の行か
				int ysec = n * DrumTimeSpanSec; // ｙ方向の時刻
				wintime ret = new wintime(DispTimeStartDrum);
				ret.inc_sec(xsec+ysec);
				return ret;
			}
		}
		return null;
	}
	/**
	 * DRUMモードで座標（View内）から表示されているｃｈを調べる
	 * @param pt
	 * @return
	 */
	private int getChannelFromPointOnDrum(Point pt){
		int xpos = pt.x;
		int ypos = pt.y;
		for(int i =0;i<DrumRect.size();i++){
			Rectangle r = DrumRect.get(i);
			if(r.contains(xpos,ypos)){
				return DrumRectCh.get(i);
			}
		}
		return -1;
	}
	/**
	 * カーソルを描画する
	 * @param gd2
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 */
	private void drawCursor(Graphics2D gd2,int x1,int y1,int x2,int y2){
		//Graphics2D gd2 = (Graphics2D)gd.create(x1,y1,x2,y2);
		//gd2.clearRect(x1,y1,x2-x1,y2-y1);
		double xmax = x2-x1;
		double ymax = y2-y1;
		
		//gd2.clipRect(0,0,(int)xmax,(int)ymax);
		gd2.clipRect(x1,y1,x2-x1,y2-y1);
		gd2.translate(x1,y1);
		gd2.setColor(ColorCursor);
		gd2.drawLine(CurrentMousePositon.x-x1,0,CurrentMousePositon.x-x1,(int)ymax);
		gd2.drawLine(0,CurrentMousePositon.y-y1,(int)xmax,CurrentMousePositon.y-y1);
		
		// カーソル位置から時間を計算
		int tmxoffset = CurrentMousePositon.x-x1;
		// calcCurrentMouseTime(tmxoffset,x2-x1);
		//		CurrentMouseTime = x2time(tmxoffset,x2-x1,DispTimeStart,DispTimeRange_ms);
		
		//CurrentMouseChannelIndex = (int)((float)GraphNum * (float)(CurrentMousePositon.y-y1) / (float)ymax);
			
		if(DrawCursorTimeInfo){
			Font fon2 = gd2.getFont().deriveFont(12f);
			gd2.setFont(fon2);
			int pos = drawTools.POSITION_LEFT_BOTTOM | drawTools.FILL_RECT;
			if((x2-x1)/2 < tmxoffset) pos = drawTools.POSITION_RIGHT_BOTTOM | drawTools.FILL_RECT;
			drawTools.drawText(gd2, CurrentMouseTime.fmtDay() + " " + CurrentMouseTime.fmtuSec(), CurrentMousePositon.x-x1, (int)ymax-1, pos);
		}
		
		gd2.setClip(null);
		gd2.translate(-x1,-y1);
	}

	// 一秒以下の最大値、最小値をモニタするために
	private int DrawData2Max = Integer.MIN_VALUE;
	private int DrawData2Min = Integer.MAX_VALUE;
	private long DrawData2MaxSpan = Integer.MIN_VALUE;
	
	/**
	 * データを書く
	 * @param gd2
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param space
	 * @param minamp
	 * @param maxamp
	 * @param d
	 * @param tm
	 * @param continuum
	 * @param lastData
	 * @param dataColor
	 * @param cidx
	 */
	private void drawData2(Graphics2D gd2, int x1,int y1,int x2, int y2,int space,
			int minamp,int maxamp,int[] d,wintime tm,boolean continuum,int lastData,Color dataColor,int cidx) {
		//Graphics2D gd2 = (Graphics2D)gd.create(x1,y1+space,x2-space,y2-space);
		float xmax = x2-x1-space;
		//float ymax = y2-y1-space*2;
		float ymax = y2-y1;
		//if(ClipRect) gd2.clipRect(x1,y1+space,x2-x1+1,y2-y1-space*2+1);
		if(ClipRect) gd2.clipRect(x1,y1,x2-x1+1,y2-y1+1);
		//gd2.translate(x1,y1+space);
		gd2.translate(x1,y1);
		gd2.setColor(dataColor);
		int center = (int)(((long)maxamp + (long)minamp)/2L);
		int amp   =  (int)(((long)maxamp-(long)minamp)/2);
		if(amp == 0) amp = 1;
		
		double yd1max,yd1min,yd1end,yd2start;
		yd1max = yd1min = yd1end = yd2start = 0.0f;
		int xi1, xi2;
		xi1 = xi2 = 0;
		int state =0;
		double ctime = tm.getValueS()*1000;	// 現在時刻
		double ctimesmple = 1000.0f/d.length;	// サンプル周期
		double cstart = GVDispTimeStart.getValueS()*1000+GVDispTimeStart.msec();	//	表示スタート時刻
		double cend   = cstart + GVDispTimeRange_ms ;								// 	表示終了時刻

		for(int i=0;i<d.length;i++,ctime += ctimesmple){
			if(ctime < cstart) continue;
			if(ctime > cend) break;
			
			if(d[i]>DrawData2Max) DrawData2Max = d[i];
			if(d[i]<DrawData2Min) DrawData2Min = d[i];
			
			double xnow = (ctime - cstart)/(cend-cstart)*xmax ;
			double ynow = -((long)d[i]- center)*ymax/2/amp + ymax/2;
			switch(state){
			case 0:
				xi1 = (int)xnow;
				yd1max = yd1min = yd1end = ynow;
				state = 1;
				if(continuum){
					double yold = -(lastData- center)*ymax/2/amp + ymax/2;
					double xold = xnow - 1000.0f*xmax/((cend-cstart)*d.length);
					gd2.drawLine((int)xold,(int)yold,(int)xnow,(int)ynow);	
					if(cidx == i) drawDataMarker(gd2,(int)xnow,(int)ynow);
				}
				break;
			case 1:
				if((int)xi1 == (int)xnow){
					if(ynow > yd1max) yd1max = ynow;
					if(ynow < yd1min) yd1min = ynow;
					yd1end = ynow;
					if(i==d.length-1){
						gd2.drawLine(xi1,(int)yd1min,xi1,(int)yd1max);	
						if(cidx == i) drawDataMarker(gd2,(int)xi1,(int)yd1max);
					}
				}
				else{
					gd2.drawLine(xi1,(int)yd1min,xi1,(int)yd1max);	
					yd2start = ynow;
					xi2 = (int)xnow;
					gd2.drawLine(xi1,(int)yd1end,xi2,(int)yd2start);	
					if(cidx == i) drawDataMarker(gd2,(int)xi2,(int)yd2start);
					xi1 = (int)xnow;
					yd1max = yd1min = yd1end = ynow;
				}

				break;
			}
			// 補助の目盛を描く
			if( i==0 ){
				drawTimeTick(gd2,(int)(xnow),(int)(ymax-4) ,tm);
			}
			else{
				// サンプル点を描画
				if(GVDispTimeRange_ms<=2000){
					if(GVDispTimeRange_ms*d.length/1000<=100){
						drawSampleTimeTick(gd2,(int)(xnow),(int)(ymax-2) ,tm);
					}
				}
			}
			
		}
		//gd2.translate(-x1,-y1-space);
		gd2.translate(-x1,-y1);
		if(ClipRect) gd2.setClip(null);
	}
	//----------------------------------------------------------------------------------------------------
	// 1秒分のデータを1本の棒で書く
	private void drawData3(Graphics2D gd, int x1,int y1,int x2, int y2,int space,
			int minamp,int maxamp,int[] d,wintime tm,Color dataColor,int datamax,int datamin,boolean continum) {
		int xmax = x2-x1-space;
		//int ymax = y2-y1-space*2;
		int ymax = y2-y1;
		int center = (int)(((long)maxamp + (long)minamp)/2L);
		int amp   =  (int)(((long)maxamp-(long)minamp)/2);
		if(amp == 0) amp = 1;
		
		int xnow = (int)((float)(tm.getValueS() - GVDispTimeStart.getValueS())*(float)xmax*1000f/(float)GVDispTimeRange_ms);
		int ynowmax = (int)(-((float)datamax- (float)center)*(float)ymax/2/(float)amp + (float)ymax/2);
		int ynowmin = (int)(-((float)datamin- (float)center)*(float)ymax/2/(float)amp + (float)ymax/2);
		
		if(DispData3LastYmax < (int)ynowmin) DispData3LastYmax = (int)ynowmin;
		if(DispData3LastYmin > (int)ynowmax) DispData3LastYmin = (int)ynowmax;

		if(datamax>DrawData2Max) DrawData2Max = datamax; //
		if(datamin<DrawData2Min) DrawData2Min = datamin;	//

		
		//if(!continum) DispData3ContinumHold = continum;
		if(DispData3LastX != (int)xnow){
			if(DrawMean){
				gd.drawLine((int)xnow,(DispData3LastYmax+DispData3LastYmin)/2,(int)xnow,(DispData3LastYmax+DispData3LastYmin)/2+1);	
			}
			else{
				gd.drawLine((int)xnow,DispData3LastYmax,(int)xnow,DispData3LastYmin);
				if((DispData3LastX+1) == (int)xnow){
					if(DispData3FromMax){
						gd.drawLine((int)DispData3LastX,DispData3PreYmax,(int)xnow,DispData3LastYmin);
						DispData3FromMax = false;
					}
					else{
						gd.drawLine((int)DispData3LastX,DispData3PreYmin,(int)xnow,DispData3LastYmax);
						DispData3FromMax = true;
					}
				}
				//DispData3ContinumHold=true;
				DispData3PreYmax = DispData3LastYmax;
				DispData3PreYmin = DispData3LastYmin;
			}
			// 補助の目盛を描く
			DispData3LastX = (int)xnow;
			DispData3LastYmax=Integer.MIN_VALUE;
			DispData3LastYmin=Integer.MAX_VALUE;
		}
	}
	// drawData3の最初と最後の処理
	private void drawData3EnterEnd(boolean ent,Graphics2D gd,int x1,int y1,int x2,int y2,int space,Color dataColor){
		if(ent){
			if(ClipRect) gd.clipRect(x1,y1+space,x2-x1+1,y2-y1-space*2+1);
			gd.translate(x1,y1+space);
			gd.setColor(dataColor);
			DispData3LastYmax=Integer.MIN_VALUE;
			DispData3LastYmin=Integer.MAX_VALUE;

		}
		else{
			if(ClipRect) gd.setClip(null);
			gd.translate(-x1,-y1-space);
		}
	}
	
	
	// データマーカを書く
	private void drawDataMarker(Graphics2D g,int x,int y){
		if(CursorDisplay==0) return;
		if(GraphHeight < 30) return;
		int [] px = new int[4];
		int [] py = new int[4];
		int size = 3;
		px[0] = x;
		px[1] = x+size;
		px[2] = x;
		px[3] = x-size;
		py[0] = y-size;
		py[1] = y;
		py[2] = y+size;
		py[3] = y;
		g.fillPolygon(px,py,px.length);
		
	}
	
	
	// ------------------------------------------
	// ドラッグマーカーを描く
	private void drawDraggMarker(Graphics2D gd,int x1,int y1,int x2,int y2){
		if(TimeSpanDraggPhase == 0) return;
		Graphics2D gd2 = (Graphics2D)gd.create(x1,y1,x2,y2);
		//gd2.clearRect(x1,y1,x2-x1,y2-y1);
		double xmax = x2-x1;
		double ymax = y2-y1;
		gd2.clipRect(0,0,(int)xmax,(int)ymax);
		
		gd2.setColor(ColorXDragging);
		int xstart ,width;
		
		if(DraggStart.x > DraggEnd.x){ 	width = DraggStart.x - DraggEnd.x ; xstart = DraggEnd.x;}
		else						 {	width = DraggEnd.x - DraggStart.x ; xstart = DraggStart.x; }
		gd2.fillRect(xstart-x1,0,width,(int)ymax);
		if(TimeSpanDraggPhase==3){
			TimeSpanDraggPhase=0;
			int xdelta = (int)DraggStart.getX()-(int)DraggEnd.getX();
			if(-5 >= xdelta || xdelta >= 5){ 
				wintime t1 = x2time(xstart-x1,		x2-x1,	GVDispTimeStart,	GVDispTimeRange_ms);
				wintime t2 = x2time(xstart-x1+width,x2-x1,	GVDispTimeStart,	GVDispTimeRange_ms);
				//System.out.println("t1=" + t1.fmt());
				//System.out.println("t2=" + t2.fmt());
				wintime t1s = new wintime(t1.year(),t1.month(),t1.day(),t1.hour(),t1.min(),t1.sec(),10*(t1.msec()/10));
				
				GVDispTimeStart=t1s;
				GVDispTimeRange_ms = (int)(t2.getValue() - t1.getValue());
				GVDispTimeRange_ms = 10*(GVDispTimeRange_ms / 10);
				if(GVDispTimeRange_ms<50) GVDispTimeRange_ms=50;
				setDispTimeRange_ms(GVDispTimeRange_ms);
				// if(DispTimeRange_ms<1000) DispTimeRange_ms=1000;
				onDispTimeChange();
				repaintAll();
			}
		}
	}
	// ------------------------------------------
	// misc情報を描く
	//private void drawMisc(Graphics2D gd,int x1,int y1,int x2,int y2){
	private void drawMisc(){
		
		//String mes1 = String.format("UDP Rcv=%d/Rty=%d/%d ", PacketCounter,UdpRetryCounter,UdpRetryRecvCounter);
		//gd.drawString(mes,1,y2-meshight*3);
	
//		String mes2  = "@=" + CurrentMouseTime.fmtuSec();
//		mes2 += String.format(" #ch=%d/%d ",GraphNum,(int)getChannelNumber());
		//gd.drawString(mes1+mes2,1,y2-meshight*1-1);
		
		//String mes3 = String.format(" Draw=%dms:%d/%d",PaintTimeCurrent,DrawMode,DrawPoint);
		//String mes4 = String.format(" MemFree/Max=%,dMB/%,dMB (%3.1f%% free) ",DataBase.MemoryRemain/1024,DataBase.MemoryMax/1024,100f*(float)DataBase.MemoryRemain/(float)DataBase.MemoryMax);
		//if(DataBase.MemoryMax<512*1024){
		//	mes4 += " <<< Warnning: Total Memory is Too Small ! Please see Help>>>"; 
		//}
		//ParentPanel.setInformation1(mes4+mes1+mes2+mes3);
		//ParentPanel.setInformation1(mes3);
		
		ParentPanel.setUdpRcvStatus(PacketCounter,UdpRetryCounter,UdpRetryRecvCounter);
		ParentPanel.setFreeMemory(DataBase.MemoryRemain,DataBase.MemoryMax);
		
	}
	// -------------------------------------------------------------------------------
	// サンプル時間の補助目盛りを書く
	private void drawSampleTimeTick(Graphics2D gd2,int x1,int y1,wintime tm){
		if(!ShowTimeSamplePoint) return;
		Color dataColor = gd2.getColor();
		gd2.setColor(ColorTimeSample);
		gd2.drawLine(x1,y1-4,x1,y1); 
		gd2.setColor(dataColor);
	}
	// ------------------------------------------
	// シフトUP,DOWNアイコンを描く
	private void drawShiftIcon(int up,Graphics2D gd,int x1,int y1,int width,int height){
		int [] xp = new int [3];
		int [] yp = new int [3];
		if(up != 0){
			xp[0] = x1 + width / 2; 	yp[0]=y1;
			xp[1] = x1; 				yp[1]=y1 + height;
			xp[2] = x1 + width; 		yp[2]=y1 + height;
			
		}
		else{
			xp[0] = x1 + width / 2; 	yp[0]= y1 + height;
			xp[1] = x1; 				yp[1]= y1  ;
			xp[2] = x1 + width;			yp[2]= y1 ;
			
		}
		gd.fillPolygon(xp,yp,3);
		
	}
	// -------------------------------------------------------------------------------
	// 時間の補助目盛りを書く
	private void drawTimeTick(Graphics2D gd2,int x1,int y1,wintime tm){
		if(!ShowTimeSamplePoint) return;
		if(tm.min()==0 && tm.sec() == 0){
			Color dataColor = gd2.getColor();
			gd2.setColor(ColorTime1hour);
			gd2.drawLine(x1,y1-4,x1,y1); 
			gd2.setColor(dataColor);
		}
		else if(tm.sec() == 0){
			if(GVDispTimeRange_ms <1000*60*60*2){
				Color dataColor = gd2.getColor();
				gd2.setColor(ColorTime1min);
				gd2.drawLine(x1,y1-4,x1,y1); 
				gd2.setColor(dataColor);
			}
		}
		else {
			if(GVDispTimeRange_ms <1000*60*5){
				Color dataColor = gd2.getColor();
				gd2.setColor(ColorTime1Sec);
				gd2.drawLine(x1,y1-4,x1,y1); 
				gd2.setColor(dataColor);
			}
		}
		
	}
//---------------------------------------------------------------------------------
	// 時間軸を書く
	private void drawXaxis2(Graphics2D gd, int x1, int y1,int x2,int y2,int space) {
		Graphics2D gd2 = (Graphics2D)gd.create(x1,y1,x2-space,y2);
		int xsize = x2-x1 - space;
		int ysize = y2-y1;
		gd2.setColor(ColorXAxis);
		int yline = 10;
		gd2.drawLine(0,yline,xsize,yline);
		
		// メモリの表示間隔を決める
		int tick = (int)(GVDispTimeRange_ms / 6);
		int subtick = 0;
		if(tick <= 2) 				{tick = 2; 		subtick=1;		}
		else if(tick <= 5)			{tick = 5;		subtick=1;		}
		else if(tick <= 10)			{tick = 10;		subtick=1;		}
		else if(tick <= 20)			{tick = 20;		subtick=2;		}
		else if(tick <= 50)			{tick = 50;		subtick=5;		}
		else if(tick <= 100)		{tick = 100;	subtick=10;		}
		else if(tick <= 200)		{tick = 200;	subtick=20;		}
		else if(tick <= 500)		{tick = 500;	subtick=50;		}
		else if(tick <= 1000)		{tick = 1000;	subtick=100;			}
		else if(tick <= 2000)		{tick = 2000;	subtick=200;				}
		else if(tick <= 5000)		{tick = 5000;	subtick=500;				}
		else if(tick <= 10*1000)	{tick = 10*1000;	subtick=1000;	}
		else if(tick <= 20*1000)	{tick = 20*1000;	subtick=2000;	}
		else if(tick <= 30*1000)	{tick = 30*1000;	subtick=3000;	}
		else if(tick <= 60*1000)	{tick = 60*1000;	subtick=6000;	}
		else if(tick <= 2*60*1000)	{tick = 2*60*1000;	subtick=2*6000;	}
		else if(tick <= 5*60*1000)	{tick = 5*60*1000;	subtick=5*6000;	}
		else if(tick <= 10*60*1000)	{tick = 10*60*1000;	subtick=10*6000;	}
		else if(tick <= 30*60*1000)	{tick = 30*60*1000;	subtick=30*6000;	}
		else if(tick <= 60*60*1000)	{tick = 60*60*1000;	subtick=60*6000;	}
		else if(tick <= 3*60*60*1000)	{tick = 3*60*60*1000;	 subtick=3*60*6000;	}
		else if(tick <= 10*60*60*1000)	{tick = 10*60*60*1000;	subtick=10*60*6000;	}
		else 						{tick = 30*60*60*1000;		subtick=30*60*6000;	}
		
		// ---------------------
		// draw Date
		wintime curtime = new wintime(GVDispTimeStart);
		float fp =  (float)getFontPoint((ysize-yline)/2);
		Font fon = gd2.getFont().deriveFont(fp);
		gd2.setFont(fon);
		int fontHight= gd2.getFontMetrics().getHeight();
		
		// 目盛りを描きながら、時刻をマップに保存する
		int curtimeofs = 0;
		long ms = curtime.getValue();
		int sute = (int)(ms % (long)subtick);
		curtimeofs -= (sute);
		curtime.dec_msec(sute);
		TreeMap<Integer,wintime> map  =new TreeMap<Integer,wintime>();
		while(true){
			int xpos = (int)((double)xsize * (double)curtimeofs / GVDispTimeRange_ms);
			if(xpos>=0){
				if(curtime.msec()==0) gd2.drawLine(xpos,yline-6,xpos,yline+4);
				else gd2.drawLine(xpos,yline,xpos,yline+2);
				int eva = 1000 - evaluateTime(curtime);
				eva = ((eva << 16)&0xffff0000) | xpos & 0xffff;
				map.put(eva,new wintime(curtime));
			}
			curtimeofs += subtick;
			if(curtimeofs > GVDispTimeRange_ms) break;
			curtime.inc_msec(subtick);
		}
		// 時刻のうちきりのよい時刻から表示するべき時刻を選抜する
		int dispnum = xsize / 100;
		HashMap<Integer,wintime> hmap=arrangeTimeDisplay(map,dispnum);
		// 時刻を描画する
		Iterator<Integer> ite = hmap.keySet().iterator();
		int xmin = 0x100000;
		String tm1=null;
		while(ite.hasNext()){
			int key = ite.next();
			int xpos = key & 0xffff;
			curtime = hmap.get(key);
			gd2.drawLine(xpos,yline,xpos,yline+5);
			String tm2 = curtime.fmtSec();
			if(curtime.msec()!=0) tm2 +=String.format(".%03d", curtime.msec());
			gd2.drawString(tm2,xpos,yline+fontHight);
			if(xmin>xpos){
				tm1 = curtime.fmtDay();
				//gd2.drawString(tm1,xpos,yline+fontHight*2);// 
				xmin = xpos;
			}
		}
		// TODO なぜか*2だと隙間が大きく、下のバーにかかってしまう
		// 先頭だけ日付を書く
		gd2.drawString(tm1,xmin,yline+fontHight*2);
		//---------------------------------------------------------------------
		wintime[] cwm = getDialogTimeMarker().getWinTime();
		TmMarker.clear();
		if(cwm!=null){
			if(cwm.length>=1){
				Font fon2 = gd2.getFont().deriveFont(10f);
				gd2.setFont(fon2);
				int[] idxmk = getDialogTimeMarker().getMarkerIndex();
				long dispstart =  GVDispTimeStart.getValue();
				for(int i=0;i<cwm.length;i++){
					long marker = cwm[i].getValue();
					int  us = cwm[i].usec();
					marker = marker-dispstart;
					if(marker <0 || marker>GVDispTimeRange_ms) continue;
					int xposmarker = (int)((double)xsize * ((double)marker+(double)us/1000) / GVDispTimeRange_ms);
					String mes = String.format("[%d]", idxmk[i]);
					drawTools.drawText(gd2, mes, xposmarker, yline, drawTools.POSITION_CENTER_BOTTOM);
					// if(idxmk[i]==0) xposmarker *= -1;
					Rectangle mr = new Rectangle(x1+xposmarker-5,y1+yline-10,10,10);
					TimeMarker tm = new TimeMarker();
					tm.Number = idxmk[i];
					tm.Xpos = xposmarker;
					tm.IconRect = mr;
					TmMarker.add(tm);
				}
			}
		}
		//---------------------------------------------------------------------
		gd2.dispose();
	}
	
	private float xGuideFactorms = 0f; // ms / dot
	// -------------------------------------------
	// 時間軸ガイドを描画する
	private void drawXGuide(Graphics2D gd2,int x1,int y1,int x2,int y2){
		XGuide = new Rectangle(x1,y1,x2-x1,y2-y1);
		Color orgCol = gd2.getColor();
		gd2.setColor(ColorXAxis);
		gd2.translate(x1,y1);
		int width = x2 - x1;
		int height = y2 - y1;
		
		// 表示領域の最大値
		int dispmax = width-4;
		// 保持データの最古と最新　１秒
		int data1 = DataBase.OldestTime.getValueS();
		int data2 = DataBase.LatestTime.getValueS();
		// 表示している領域
		int disp1 = GVDispTimeStart.getValueS();
		long disp2 = disp1+GVDispTimeRange_ms/1000;
		// 表示領域と保持している領域の全体を分母にする
		int start;
		if(data1<disp1) start=data1;
		else			start=disp1;
		long end;
		if(disp2>data2) end=disp2;
		else			end=data2;
		long refspan = end-start;
		if(refspan<=0) refspan=1;
		long datx1 =(long)((float)dispmax*(float)(data1-start)/(float)refspan);
		long datx2 =(long)((float)dispmax*(float)(data2-start)/(float)refspan);
		long dispx1=(long)((float)dispmax*(float)(disp1-start)/(float)refspan);
		long dispx2=(long)((float)dispmax*(float)(disp2-start)/(float)refspan);

		//gd2.fillRect(datx1,height*2/9,datx2-datx1, height*1/9);
		//gd2.drawRect(dispx1,height*2/9-3,dispx2-dispx1,height*1/9+6);
		//gd2.fillRect((int)datx1,	4,(int)(datx2-datx1), 	5);
		gd2.fillRect((int)datx1,	8,(int)(datx2-datx1), 	3);
		//gd2.drawRoundRect((int)dispx1,1,(int)(dispx2-dispx1),10,8,8);
		gd2.fillRect((int)dispx1,11,(int)(dispx2-dispx1),3);
		//gd2.drawRoundRect((int)dispx1,1,(int)(dispx2-dispx1),10,8,8);
		
		XGuideViewRange = new Rectangle((int)dispx1+x1,1+y1,(int)(dispx2-dispx1),10);
		if(xGuideViewRangeDraggingMode == 0){
			xGuideFactorms = 1000f*(float)(data2-data1)/(float)(datx2-datx1);
		}
		
		Font fonorg=gd2.getFont();
		float fp =  (float)getFontPoint((height-2)/1);
		Font fon = gd2.getFont().deriveFont(fp);
		gd2.setFont(fon);

		drawTools.drawText(gd2,DataBase.OldestTime.fmt(),	(int)datx1-2,6,drawTools.POSITION_RIGHT_CENTER);
		//drawTools.drawText(gd2,OldestTime.fmtDay(),	datx1,15+fontHight,	drawTools.POSITION_LEFT_TOP);
		drawTools.drawText(gd2,DataBase.LatestTime.fmt(),	(int)datx2+2,6,drawTools.POSITION_LEFT_CENTER);
		//drawTools.drawText(gd2,LatestTime.fmtDay(),	datx2,15+fontHight,	drawTools.POSITION_RIGHT_TOP);
		
		gd2.translate(-x1,-y1);
		gd2.setColor(orgCol);
		gd2.setFont(fonorg);
	}
	// ------------------------------------------
	// Y軸のラベルを書くかどうか
	public boolean isDrawYLabel(){
		return CanDrawNumber && DrawYLabel;
	}
	// ------------------------------------------
	// Y軸の物理量表示のフォーマットを得る
	public String getPhysicalQuantityFormat(){
		String fmt = new String(ChannelDataDisplayProperty.FmtPhysicalQuantity);
		return fmt;
	}
	// ------------------------------------------
	// Y軸の物理量表示のフォーマットを設定
	public void setPhysicalQuantityFormat(String fmt){
		ChannelDataDisplayProperty.FmtPhysicalQuantity = new String(fmt);
		repaintAll();
	}
	/**
	 * 振幅軸を描くプロパティ
	 * @author Toshiharu Ikeda <ikeda.104@gmail.com>
	 *
	 */

	public class YAxisDrawProperty {
		/**
		 * センターのラベルを描くかどうか
		 */
		public boolean DrawCenter = true;
		/**
		 * 振幅変更、シフトハンドルを描くかどうか
		 */
		public boolean  DrawControlHandle = true;
		/**
		 * ラベルフォントの最大
		 */
		public int     LabelFontMax = 12;
		/**
		 * ラベルフォントの最小
		 */
		public int     LabelFontMin = 8;
		/**
		 * ラベルの最大数 (奇数を指定すること）
		 */
		public int 		LabelNumberMax = 21;
	}
	private YAxisDrawProperty YAxisProp=new YAxisDrawProperty();
	/**
	 * 振幅軸を描くプロパティ
	 * @author Toshiharu Ikeda <ikeda.104@gmail.com>
	 *
	 */
	public class XAxisDrawProperty {
		/**
		 * X軸バーを描くかどうか
		 */
		public boolean DrawXBar = true;
		/**
		 * CLOCKを描く門か
		 */
		public boolean     DrawClock = true;
	}
	private XAxisDrawProperty XAxisProp=new XAxisDrawProperty();
	
	/**
	 * Y軸を描く
	 * @param gd
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @param space
	 * @param min
	 * @param max
	 * @param chnum
	 */
	private void drawYaxis(Graphics2D gd, int x1,int y1,int x2, int y2,int space,int min,int max,int chnum) {
		if(!DispChannelNumber) return;
		if(!DrawYAxisLabel) return;
		//Graphics2D gd2 = (Graphics2D)gd.create(x1,y1+space,x2,y2-space);
		Graphics2D gd2 = (Graphics2D)gd.create(x1,y1,x2,y2);
		double xmax = x2-x1;
		double ymax = y2-y1-space*2;
		//float fp = (float)(ymax/4*72/96.0f);
		float fp = (float)getFontPoint((int)(ymax/4.5));
		if(fp>YAxisProp.LabelFontMax){ 
			fp = (float)YAxisProp.LabelFontMax;
		}
		else if(fp < 5f){
			fp = 5f;
		}
		CanDrawNumber=true;
		if(fp<YAxisProp.LabelFontMin) CanDrawNumber=false;
		
		Font fon = gd2.getFont().deriveFont(fp);
		gd2.setFont(fon);
		gd2.setColor(ColorYAxis);
		int xmgn=12;
		gd2.drawLine((int)(xmax-space-xmgn),space,(int)(xmax-space-xmgn),(int)ymax+space);
		
		int yunit = (int)(ymax /  2/ fp);
		if(yunit < 5) yunit = 2;
		if(yunit > YAxisProp.LabelNumberMax-1){
			yunit = YAxisProp.LabelNumberMax-1;
		}
		if((yunit % 2) !=0) yunit -= 1;
		double mem = max;
		ChannelDataDisplayProperty pro = getChannelInfo(chnum);
		for(int i=0;i<=yunit;i++){
			if(i%2==0)	gd2.drawLine((int)(xmax-space-xmgn)-6,(int)(i*ymax/yunit)+space,(int)(xmax-space-xmgn),(int)(i*ymax/yunit)+space);
			else 		gd2.drawLine((int)(xmax-space-xmgn)-2,(int)(i*ymax/yunit)+space,(int)(xmax-space-xmgn),(int)(i*ymax/yunit)+space);
			if(i==yunit/2) {
				pro.mean = (int)mem;
				gd2.drawLine((int)(xmax-space-xmgn)-6,(int)(i*ymax/yunit - 0)+space,(int)(xmax-space-xmgn),(int)(i*ymax/yunit-0)+space);
				if(pro.offsetEnable){
					gd2.drawLine((int)(xmax-space-xmgn)-6,(int)(i*ymax/yunit - 1)+space,(int)(xmax-space-xmgn),(int)(i*ymax/yunit-1)+space);
					gd2.drawLine((int)(xmax-space-xmgn)-6,(int)(i*ymax/yunit + 1)+space,(int)(xmax-space-xmgn),(int)(i*ymax/yunit+1)+space);
				}
			}
			if(isDrawYLabel() && !((i==yunit/2) && !YAxisProp.DrawCenter) ){ // 高さが十分あり、かつDrawYLabeならラベルを書く
				//String memStr = String.format("%,4.0f",mem);
				String memStr = pro.convUnit(mem,false);
				//gd2.drawString(memStr, (int)(xmax-space-xmgn)-memStr.length()*fp*3/5-8, (int)(i*(ymax-fp)/yunit+fp));
				int pos = drawTools.POSITION_RIGHT_CENTER;
				// 上下の端をオフセットするのは、見栄えが悪いのでやめる。
				//if(i==0) pos = drawTools.POSITION_RIGHT_TOP;
				//else if(i==yunit) pos = drawTools.POSITION_RIGHT_BOTTOM;
				drawTools.drawText(gd2, memStr, (int)(xmax-space-xmgn)-8, (int)(i*ymax/yunit)+space, pos);
			}
			mem -= ((double)max-(double)min)/(double)yunit;
		}
		ChConfig.setData(chnum, pro);

		if(!IsPrint && YAxisProp.DrawControlHandle){
			// 小さすぎる場合は書かない
			if(!(pro.AxisShiftUp.height<=IndividualUpDownButtonMinimum || pro.AxisShiftDown.height<=IndividualUpDownButtonMinimum || pro.YAxisScaleDown.height<=IndividualUpDownButtonMinimum || pro.YAxisScaleUp.height<=IndividualUpDownButtonMinimum)){
				// 三角の上下移動アイコンを描く
				gd2.setColor(Color.gray);
				//System.out.println(String.format("y=%d,%d:x=%d,%d",pro.YAxisScaleDown.y,pro.YAxisScaleDown.height,pro.YAxisScaleDown.x,pro.YAxisScaleDown.width));
				drawShiftIcon(1,gd2,pro.AxisShiftUp.x-x1,pro.AxisShiftUp.y-y1-space,pro.AxisShiftUp.width-1,pro.AxisShiftUp.height-1);
				//gd2.drawRect(pro.AxisShiftUp.x-x1,pro.AxisShiftUp.y-y1-space,pro.AxisShiftUp.width-1,pro.AxisShiftUp.height-1);
				drawShiftIcon(0,gd2,pro.AxisShiftDown.x-x1,pro.AxisShiftDown.y-y1-space,pro.AxisShiftDown.width-1,pro.AxisShiftDown.height-1);
				//gd2.drawRect(pro.AxisShiftDown.x-x1,pro.AxisShiftDown.y-y1-space,pro.AxisShiftDown.width-1,pro.AxisShiftDown.height-1);
				// 三角の上下移動アイコンを描く
				drawZoomIcon(0,gd2,pro.YAxisScaleDown.x-x1, pro.YAxisScaleDown.y-y1-space,pro.YAxisScaleDown.width-1,	pro.YAxisScaleDown.height-1);
				//gd2.drawRect(pro.YAxisScaleDown.x-x1,	pro.YAxisScaleDown.y-y1-space,	pro.YAxisScaleDown.width-1,	pro.YAxisScaleDown.height-1);
				drawZoomIcon(1,gd2,pro.YAxisScaleUp.x-x1, pro.YAxisScaleUp.y-y1-space,pro.YAxisScaleUp.width-1,	pro.YAxisScaleUp.height-1);
				//gd2.drawRect(pro.YAxisScaleUp.x-x1,		pro.YAxisScaleUp.y-y1-space,	pro.YAxisScaleUp.width-1,	pro.YAxisScaleUp.height-1);
			}
		}
		gd2.dispose();
	}
	
	// ------------------------------------------
	// ZoomIN(+) ZoomOUT(-)アイコンを描く
	private void drawZoomIcon(int in,Graphics2D gd,int x1,int y1,int width,int height){
		gd.drawRect(x1, y1, width, height);
		if(in==0){
			gd.drawLine(x1+width/2,y1+2,x1+width/2,y1+height-2);
		}
		gd.drawLine(x1+2,y1+height/2,x1+width-2,y1+height/2);
	}
	// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
	// ファイルドロップのとき
	@Override
	public void drop(DropTargetDropEvent d) {
			TreeSet<String> ts = DataBase.Dir2Files(d);
			openFiles(ts);
	}
	// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
	
	@Override
	public void dropActionChanged(DropTargetDragEvent arg0) {
		
	}
	//---------------------------------------------------------------------------------
	// きりのいい時刻を評価する。
	private int evaluateTime(wintime tm){
		int ret = 0;
		int msec = tm.msec();
		if((msec % 10)==0){
			ret++;
			if((msec % 20)==0){
				ret++;
				if((msec % 50)==0){
					ret++;
					if((msec % 100)==0){
						ret++;
						if((msec % 200)==0){
							ret++;
							if((msec % 500)==0){
								ret++;
								if((msec % 1000)==0){
									ret++;
									if((tm.sec() % 2)==0){
										ret++;
										if((tm.sec() % 5)==0){
											ret++;
										if((tm.sec() % 10)==0){
											ret++;
											if((tm.sec() % 30)==0){
												ret++;
												if(tm.sec() == 0){
													ret++;
													if((tm.min() % 2)==0){
														ret++;
														if((tm.min() % 5)==0){
															ret++;
														if((tm.min() % 10)==0){
															ret++;
															if((tm.min() % 30)==0){
																ret++;
																if(tm.min()==0){
																	ret++;
																	if((tm.hour()%2)==0){
																		ret++;
																		if((tm.hour()%5)==0){
																			ret++;
																		if((tm.hour()%10)==0){
																			ret++;
																			if((tm.hour()%30)==0){
																				ret++;
																				if(tm.hour()==0){
																					ret++;
																					if(tm.day() % 5==0){
																						ret++;
																						if(tm.day() % 10==0){
																							ret++;
																							if(tm.day()==0){
																								ret++;
																							}
																						}
																					}
																				}
																			}
																		}
																	}
																}
															}
														}
													}
												}
													}
												}
											}
											
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return ret;
	}
	// -------------------------
	// png にエクスポート
	//   
	public String ExportImageDir = null;
	
	public boolean exportAsImage(String dir,double ratex,double ratey){
		Dimension dm = this.getSize(); 

		BufferedImage img = new BufferedImage((int)(dm.width*ratex), (int)(dm.height*ratey), BufferedImage.TYPE_INT_RGB);
		Graphics2D g = img.createGraphics();
		g.scale(ratex,ratey);
		IsPrint=true;
		paintComponent(g);
		IsPrint=false;
		
        // ファイルネームを作る
		String chname = new String();
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro != null){
					if(pro.hide == 0 && !pro.Mask){
						chname += String.format("%04x", ch);
					}
				}
			}
		}
		if(chname.isEmpty()) return false;
		wintime endtm = new wintime(GVDispTimeStart);
		endtm.inc_msec(1000*((int)(GVDispTimeRange_ms-1)/1000));
		String filename = new String();
		filename = dir + GVDispTimeStart.fmt2()+"-"+endtm.fmt2()+".png";
		//------------------------------------------------------
		// save as ダイアログ
		JFileChooser filechooser = new JFileChooser(dir);
		filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
		FileNameExtensionFilter filter = new FileNameExtensionFilter("png file", "png");
		filechooser.addChoosableFileFilter(filter);
		filechooser.setDialogTitle("Save as");
		File tmp = new File(dir + GVDispTimeStart.fmt2()+"-"+endtm.fmt2()+".png");
		filechooser.setSelectedFile(tmp);
		//filechooser.ensureFileIsVisible(tmp);
		int selected = filechooser.showSaveDialog(null);
		if ( selected == JFileChooser.APPROVE_OPTION ) {
			tmp = filechooser.getSelectedFile();
			if(tmp.exists()){
				if(JOptionPane.NO_OPTION ==  JOptionPane.showConfirmDialog(null, "Over write ?","Confirmation",JOptionPane.YES_NO_OPTION)){
					return false;
				}
			}
			filename = tmp.getAbsolutePath();
			ExportImageDir = new String(tmp.getParentFile().getAbsolutePath()+File.separator);
		}
		else{
			return false;
		}
		//------------------------------------------------------
		File ofile = new File(filename);
		try {
			// TODO:FIX pngの初期フォルダがwindows/system32になることがあり、そのままOKすると例外が発生する問題
			ofile.createNewFile();
			ImageIO.write(img, "png",ofile);
		} 
	    catch (Exception f ){
	    	//e.printStackTrace();
	    	JOptionPane.showMessageDialog(null, "File Output Error\n" + ofile.getAbsolutePath(), "Error", JOptionPane.ERROR_MESSAGE); 
	    	return false;
	    }
	  //  } catch (IOException e) {
	  //  	JOptionPane.showMessageDialog(null, "File Output Error", "Error", JOptionPane.ERROR_MESSAGE); 
	//	}
		return true;
	}
	/**
	 * WIN A0にエクスポートする
	 * @param st
	 * @param en
	 * @return
	 */
	public int exportWINA0(wintime st,wintime en){
		int retcounter=0;
		if(en.getValueS() < st.getValueS()) return -4;
		// 表示されているｃｈのリストを作成する
		JFileChooser filechooser = new JFileChooser(ExportFolder);
		filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
		//FileNameExtensionFilter filter = new FileNameExtensionFilter("win A0", "");
		//filechooser.addChoosableFileFilter(filter);
		filechooser.setDialogTitle("Save as　: " + st.fmt()+" - "+en.fmt());
		File tmp = new File("000.00");
		filechooser.setSelectedFile(tmp);
		
		
		int selected = filechooser.showSaveDialog(null);
		File file;
		if ( selected == JFileChooser.APPROVE_OPTION ) {
		   file = filechooser.getSelectedFile();
		}
		else return -2;
		// ----------------------------------------
		String str = JOptionPane.showInputDialog(this,"Specify shift time[s].\n default value = 0.0",0.0);
		double timeShift = 0.0;
		try{
			timeShift = Double.valueOf(str);
		} catch(NumberFormatException e){
		}//

		
		setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR )));
		//ChannelPropDialog = new DialogChannelProperty((Frame)(this.getParent().getParent().getParent()));
		// ----------------------------------------
		ArrayList<Integer> hs = new ArrayList<Integer>();
		synchronized(DataBaseLock()){
			Iterator<Integer> chite = iteratorChannel();
			while(chite.hasNext()){
				int ch = chite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null){
					continue;
				}
				if(pro.hide==0 && !pro.Mask){
					hs.add(ch);
				}
			}
		}
		retcounter=DataBase.exportWINA0(st, en,file,hs,timeShift);
			
		setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
		return retcounter;
	}
	/**
	 * データ全部をWIN A0にエクスポートする
	 */
	public void exportWINA0all() {
		wintime st = new wintime(DataBase.OldestTime);
		wintime et = new wintime(DataBase.LatestTime);
		exportWINA0(st,et);
	}
	/**
	 * 表示範囲をWIN A0にエクスポートする
	 */
	public void exportWINA0display() {
		wintime endtm = new wintime(GVDispTimeStart);
		endtm.inc_msec(1000*((int)(GVDispTimeRange_ms-1)/1000));
		exportWINA0(GVDispTimeStart,endtm);
	}

	/**
	 * CSVにエクスポートする
	 * @param st
	 * 開始時刻
	 * @param en
	 * 終了時刻
	 * @param separator
	 * セパレータ文字列
	 * @return
	 * 成功すると出力した行数を返す。エラーは<0。
	 */
	public int exportCSV(wintime st,wintime en,String separator){
		int retcounter=0;
		if(en.getValueS() < st.getValueS()) return -4;
		// 表示されているｃｈのリストを作成する
//		try{
			JFileChooser filechooser = new JFileChooser(ExportFolder);
			filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
			FileNameExtensionFilter filter = new FileNameExtensionFilter("CSV file", "csv");
			filechooser.addChoosableFileFilter(filter);
			filechooser.setDialogTitle("Save as");
			File tmp = new File("NoName.csv");
			filechooser.setSelectedFile(tmp);
			
			
			int selected = filechooser.showSaveDialog(null);
			File file;
			if ( selected == JFileChooser.APPROVE_OPTION ) {
			   file = filechooser.getSelectedFile();
			}
			else return -2;
			// ----------------------------------------
			if(CsvOptionDlg == null){
				CsvOptionDlg = new CsvOptionDialog(getParentFrame2());
			}
			CsvOptionDlg.setMode(0);
			CsvOptionDlg.CsvHeader=CsvDispHeader;
			CsvOptionDlg.CsvTime=CsvDispTime;
			CsvOptionDlg.MillisecondSepComma = CsvMillisecondComma;
			CsvOptionDlg.updateData(true);
			CsvOptionDlg.setVisible(true);
			if(!CsvOptionDlg.OK){
				return 0;
			}
			CsvOptionDlg.updateData(false);
			CsvDispHeader = CsvOptionDlg.CsvHeader;
			CsvDispTime = CsvOptionDlg.CsvTime;
			CsvMillisecondComma = CsvOptionDlg.MillisecondSepComma;
			double timeShift = CsvOptionDlg.TimeShift;
			
			int decimation= CsvOptionDlg.Decimation;
			int decimationMode= CsvOptionDlg.DecimationMode;	// 0:simple,1:Median,2:mean
			
			boolean phy = CsvOptionDlg.Physical;
			
			setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR )));
			//ChannelPropDialog = new DialogChannelProperty((Frame)(this.getParent().getParent().getParent()));
			// ----------------------------------------
			ArrayList<Integer> hs = new ArrayList<Integer>();
			ArrayList<ChannelDataDisplayProperty>  fc = new ArrayList<ChannelDataDisplayProperty>();
			synchronized(DataBaseLock()){
				Iterator<Integer> chite = iteratorChannel();
				while(chite.hasNext()){
					int ch = chite.next();
					ChannelDataDisplayProperty pro = getChannelInfo(ch);
					if(pro == null){
						continue;
					}
					if(pro.hide==0 && !pro.Mask){
						hs.add(ch);
						//ChannelDataDisplayProperty p = new ChannelDataDisplayProperty(pro);
						fc.add(pro);
					}
				}
			}
			if(!phy) fc = null;
			retcounter=DataBase.exportCSV(st, en, separator, file, hs, CsvDispHeader, CsvDispTime, CsvMillisecondComma,timeShift,true,decimation,decimationMode,fc);
			
		setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
		return retcounter;
	}
	/**
	 * Binaryにエクスポートする
	 * @param st
	 * 開始時刻
	 * @param en
	 * 終了時刻
	 * @param separator
	 * セパレータ文字列
	 * @return
	 * 成功すると出力した行数を返す。エラーは<0。
	 */
	public int exportBin(wintime st,wintime en,String separator){
		int retcounter=0;
		if(en.getValueS() < st.getValueS()) return -4;
		// 表示されているｃｈのリストを作成する

		JFileChooser filechooser = new JFileChooser(ExportFolder);
		filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
		FileNameExtensionFilter filter = new FileNameExtensionFilter("Binary file", "bin");
		filechooser.addChoosableFileFilter(filter);
		filechooser.setDialogTitle("Save as");
		File tmp = new File("NoName.bin");
		filechooser.setSelectedFile(tmp);
		
		
		int selected = filechooser.showSaveDialog(null);
		File file;
		if ( selected == JFileChooser.APPROVE_OPTION ) {
		   file = filechooser.getSelectedFile();
		}
		else return -2;
		// ----------------------------------------
		if(CsvOptionDlg == null){
			CsvOptionDlg = new CsvOptionDialog(getParentFrame2());
		}
		CsvOptionDlg.setMode(1);
		CsvOptionDlg.LittleEndian = LittleEndian;
		CsvOptionDlg.updateData(true);
		CsvOptionDlg.setVisible(true);
		if(!CsvOptionDlg.OK){
			return 0;
		}
		CsvOptionDlg.updateData(false);
		LittleEndian = CsvOptionDlg.LittleEndian;
		
		double timeShift = CsvOptionDlg.TimeShift;
		
		int decimation= CsvOptionDlg.Decimation;
		int decimationMode= CsvOptionDlg.DecimationMode;	// 0:simple,1:Median,2:mean
		
		
		setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR )));
		
		// ----------------------------------------
		ArrayList<Integer> hs = new ArrayList<Integer>();
		synchronized(DataBaseLock()){
			Iterator<Integer> chite = iteratorChannel();
			while(chite.hasNext()){
				int ch = chite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null){
					continue;
				}
				if(pro.hide==0 && !pro.Mask){
					hs.add(ch);
				}
			}
		}
		// ----------------------------------------
		
		retcounter=DataBase.exportBin(st, en,  file, hs, timeShift,true,decimation,decimationMode,LittleEndian);
		// ----------------------------------------
		// インフォメーションファイルを作成する
		try {
			String inftxtname = file.getCanonicalPath()+".info";
			File inftxtfile = new File(inftxtname);
			FileWriter filewriter = new FileWriter(inftxtfile);
			filewriter.write("*** winchkg export binary file infomation \n"); 
			                //0123456789012345678901234567890123456789
			filewriter.write("Output file name         = " + file.getCanonicalPath() + "\n");
			int chmax = hs.size();
			                              //0123456789012345678901234567890123456789
			filewriter.write(String.format("Total Channel            = %d\n",chmax));
			                       //0123456789012345678901234567890123456789
			String mes = new String("Channel                  = ");
			Iterator<Integer> ite = hs.iterator();
			int ch;
			while(ite.hasNext()){
				ch = ite.next();
				mes += String.format(" %04X", ch);
			}
			filewriter.write(mes + "\n");
			
			int samplerate=0;
			int dispindex = DisplayChannel[0];
			samplerate = DataSampleRate[dispindex];
				                          //0123456789012345678901234567890123456789
			filewriter.write(String.format("Sample rate(original)    = %d Hz\n", samplerate));
			filewriter.write(String.format("Decimation ratio         = 1/%d \n", decimation));
			                        //0123456789012345678901234567890123456789
			String mes2 = new String("Decimation mode          = ");
			if(decimationMode==0){
				mes2 += "Simple";
			}
			else if(decimationMode==1){
				mes2 += "Median";
			}
			else if(decimationMode==2){
				mes2 += "Arithmetic Mean";
			}
			filewriter.write(mes2+"\n");
			                //0123456789012345678901234567890123456789
			filewriter.write("Start time(original)     = " + st.fmt() + "\n");
			filewriter.write("End   time(original)     = " + en.fmt() + "\n");
			                              //0123456789012345678901234567890123456789
			filewriter.write(String.format("Time Shift               = %g sec\n",timeShift));
			
			filewriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		// ----------------------------------------
		
		setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
		return retcounter;
	}
	/**
	 *  データのエクスポート CSV 全部
	 */
	public void exportCSVall() {
		exportCSVall(new String(","));
	}
	/**
	 *  データのエクスポート CSV 全部
	 * @param separator
	 */
	public void exportCSVall(String separator) {
		/*int lines = */exportCSV(DataBase.OldestTime,DataBase.LatestTime,separator);
		//if(lines>0) JOptionPane.showMessageDialog(this,String.format("Export success %,d lines\n", lines)+DataBase.OldestTime.fmt()+"-"+DataBase.LatestTime.fmt());
	}
	/**
	 *  データのエクスポート 全部
	 */
	public void exportBinall() {
		exportBinall(new String(","));
	}
	/**
	 *  データのエクスポート 全部
	 * @param separator
	 */
	public void exportBinall(String separator) {
		/*int lines = */exportBin(DataBase.OldestTime,DataBase.LatestTime,separator);
		//if(lines>0) JOptionPane.showMessageDialog(this,String.format("Export success %,d lines\n", lines)+DataBase.OldestTime.fmt()+"-"+DataBase.LatestTime.fmt());
	}
	/**
	 * データのエクスポート 表示範囲（秒単位に切り上げ）
	 */
	public void exportCSVdisplay() {
		exportCSVdisplay(new String(","));
	}
	/**
	 * データのエクスポート 表示範囲（秒単位に切り上げ）
	 * @param separator
	 */
	public void exportCSVdisplay(String separator) {
		wintime endtm = new wintime(GVDispTimeStart);
		endtm.inc_msec(1000*((int)(GVDispTimeRange_ms-1)/1000));
		/*int lines = */exportCSV(GVDispTimeStart,endtm,separator);
		//if(lines>0) JOptionPane.showMessageDialog(null,String.format("Export success %,d lines\n", lines)+DispTimeStart.fmt()+"-"+endtm.fmt());
	}

	
	/**
	 * データのエクスポート 表示範囲（秒単位に切り上げ）
	 */
	public void exportBindisplay() {
		exportBindisplay(new String(","));
	
	}
	/**
	 * データのエクスポート 表示範囲（秒単位に切り上げ）
	 * @param separator
	 */
	public void exportBindisplay(String separator) {
		wintime endtm = new wintime(GVDispTimeStart);
		endtm.inc_msec(1000*((int)(GVDispTimeRange_ms-1)/1000));
		/*int lines = */exportBin(GVDispTimeStart,endtm,separator);
		//if(lines>0) JOptionPane.showMessageDialog(null,String.format("Export success %,d lines\n", lines)+DispTimeStart.fmt()+"-"+endtm.fmt());
	}

	
	/**
	 * σをエクスポートする
	 * @param st
	 * @param en
	 * @param separator
	 * @return
	 */
	public int exportCSVSigma(wintime st,wintime en,String separator){
		int retcounter=0;
		CSVSigmaOutputResultMessage = "init";
		if(en.getValueS() < st.getValueS()) return -4;
		// 表示されているｃｈのリストを作成する
		try{
			JFileChooser filechooser = new JFileChooser(ExportFolder);
			filechooser.setFileSelectionMode( JFileChooser.FILES_ONLY);	      
			FileNameExtensionFilter filter = new FileNameExtensionFilter("CSV file", "csv");
			filechooser.addChoosableFileFilter(filter);
			filechooser.setDialogTitle("Save as");
			File tmp = new File("NoNameSigma.csv");
			filechooser.setSelectedFile(tmp);
			
			int selected = filechooser.showSaveDialog(null);
			File file;
			if ( selected == JFileChooser.APPROVE_OPTION ) {
			   file = filechooser.getSelectedFile();
			}
			else return -2;
//			// ----------------------------------------
//			// ----------------------------------------
			
			if(CsvSigmaOptionDialog == null){
				Frame parent = getParentFrame2();
				CsvSigmaOptionDialog = new CsvSigmaOptionDialog(parent);
			}
			CsvSigmaOptionDialog.Header=CsvDispHeader;
			CsvSigmaOptionDialog.Time=CsvDispTime;
			CsvSigmaOptionDialog.ConvSNR = isConvSigmaSNR();
			CsvSigmaOptionDialog.updateData(true);
			CsvSigmaOptionDialog.setVisible(true);
			if(!CsvSigmaOptionDialog.OK){
				return 0;
			}
			CsvSigmaOptionDialog.updateData(false);
			CsvDispHeader = CsvSigmaOptionDialog.Header;
			CsvDispTime = CsvSigmaOptionDialog.Time;
			boolean isDataNormal = CsvSigmaOptionDialog.IsNormalData;
			int NormalDataCh = CsvSigmaOptionDialog.NormalDataCh;
			setConvSigmaSNR(CsvSigmaOptionDialog.ConvSNR);
			
			// --------------------------------
			// ファイルをオープン
			FileWriter fw=null;
			try{
				fw = new FileWriter(file);
			} catch(IOException e){
				e.printStackTrace();
				return -3;
			}
			
//			String mes = new String("Time");
			ArrayList<Integer> hs = new ArrayList<Integer>();
			synchronized(DataBaseLock()){
				Iterator<Integer> chite = iteratorChannel();
				while(chite.hasNext()){
					int ch = chite.next();
					ChannelDataDisplayProperty pro = getChannelInfo(ch);
					if(pro == null){
						continue;
					}
					if(pro.hide==0 && !pro.Mask){
//						mes += separator + String.format("\"%04x\"", ch);
						hs.add(ch);
					}
				}
			}
//			if(CsvDispHeader) fw.write(mes+"\n");
		
			//---------------------------------------------------------------
			double[] min = new double[hs.size()];
			double[] max = new double[hs.size()];
			CSVSigmaOutputResultMessage = "ch = ";
			//---------------------------------------------------------------
			
			//System.out.println(mes);
			setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)));
			
			wintime tm = new wintime(st.year(),st.month(),st.day(),st.hour(),st.min(),0,0);
			int chindex;
			boolean first=true;
			int latesttime = en.getValueS();
			// 時間順にループ
			
			int medianFilter=0;
			switch(CsvSigmaOptionDialog.FilterMode){
			case 1: medianFilter = 3; break;	// Median 3
			case 2: medianFilter = 5; break;	// Median 5
			case 3: medianFilter = 11; break;	// Median 11
			case 4: medianFilter = 31; break;	// Median 31
			}
			int maxdelay = 31;
			HashMap<Integer,Median> mp = new HashMap<Integer,Median>();
			
			Iterator<Integer> ite2 = hs.iterator();
			while(ite2.hasNext()){
				int ch = ite2.next();
				Median m=null;
				if(isDataNormal && (ch == NormalDataCh) ){
					m = new Median(1);
				}
				else{
					m = new Median(medianFilter);
				}
				mp.put(ch,m);
			}				
			//int chcount = hs.size();
			LinkedList<String> timefield = new LinkedList<String>();
			int linecount=0;
			int[] channelOrder=null;
			while(tm.getValueS() <= latesttime){
				// 一秒分を単位にループ
				Iterator<Integer> ite = hs.iterator();
				//String mesout = new String(tm.fmt());
				timefield.add(new String(tm.fmt()));
				chindex=0;
				while(ite.hasNext()){
					int ch=ite.next();
					double val = 0.0;
					ChannelOutlineDataCollection dcol = DataBase.getChannelDataCollection60(ch);
					if(isDataNormal && (ch == NormalDataCh) ){
						if(dcol!=null){
							ChannelOutlineData cd = dcol.getChannelData(tm);
							if(cd != null){
								val = cd.getMean();
							}
						}
					}
					else {
						if(dcol!=null){
							ChannelOutlineData cd = dcol.getChannelData(tm);
							if(cd != null){
								val = cd.getValiance();
							}
						}
						val = Math.sqrt(val);
						
						if(ConvSigmaSNR){
							 ChannelDataDisplayProperty pro = getChannelInfo(ch);
							 double fs = (double)pro.FullScale; 
							 val = 20*Math.log10(val/fs);
						}
					}
					//if(medianFilter>0){
						mp.get(ch).input(val);
					//}
				
					
					//mesout += String.format(",%f",val);
					if(first){
						min[chindex]=max[chindex]=val;
						//CSVSigmaOutputResultMessage += String.format("  %04x",ch);
					}
					else{
						if(min[chindex]>val) min[chindex] = val;
						if(max[chindex]<val) max[chindex] = val;
					}
					chindex++;
					
				}
				if(first){
					first = false;
					channelOrder = exportCSVSigmaGetChannelOrder(mp);
					String mes = "Time";
					for(int ll=0;ll<channelOrder.length;ll++){
						mes += String.format(",\"%04x\"", channelOrder[ll]);
					}
					if(CsvDispHeader) fw.write(mes+"\n");
				}
				retcounter++;
				if(retcounter>maxdelay){
					// 適当な量が溜まったら出力を始める
					exportCSVSigmaWriteFile(fw,channelOrder,mp,timefield,false);
				}

				//fw.write(mesout+"\n");
				tm.inc_min();
			}
			// flushして出力する。
			exportCSVSigmaWriteFile(fw,channelOrder,mp,timefield,true);
			fw.close();
			//---------------------------------------------------------------
			CSVSigmaOutputResultMessage += "\nMax=";
			for(int i=0;i<max.length;i++){
				CSVSigmaOutputResultMessage += String.format(" %5.2f",max[i]);
			}
			CSVSigmaOutputResultMessage += "\nMin=";
			for(int i=0;i<min.length;i++){
				CSVSigmaOutputResultMessage += String.format(" %5.2f",min[i]);
			}
			//---------------------------------------------------------------
		} catch (IOException e){
			e.printStackTrace();
			setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
			retcounter = -1;
		}
		setCursor((Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR )));
		return retcounter;
	}
	/**
	 * 出力するＣＨの順番を決める。
	 * @param mp
	 * @return
	 */
	private int[] exportCSVSigmaGetChannelOrder(HashMap<Integer,Median> mp){
		TreeMap<Integer,Integer> chtable = new TreeMap<Integer,Integer>();
		// フィルターデータ幅でｃｈの配列をソートする （温度（フィルタしない）を先頭に持ってくるため。
		Set<Integer> k= mp.keySet();
		Iterator<Integer> ite=k.iterator();
		while(ite.hasNext()){
			int key = ite.next();
			Median md = mp.get(key);
			chtable.put(md.getWidth()*0x10000+key, key); // データ幅でソートする
		}
		int[] ch = new int[k.size()];
		Set<Integer> kid = chtable.keySet();
		Iterator<Integer> itech = kid.iterator();
		int idx = 0;
		while(itech.hasNext()){
			int kkk = itech.next();
			ch[idx++] = kkk & 0xffff;
		}
		
		
		return ch;
	}
	/**
	 * 
	 * @param fw
	 * @param ch
	 * @param mp
	 * @param timefield
	 * @throws IOException 
	 */
	private void exportCSVSigmaWriteFile(FileWriter fw,int[] ch,HashMap<Integer,Median> mp ,LinkedList<String> timefield,boolean flush) throws IOException{
		/**
		 * 読み込める数を得る
		 */
		int min = 0x1000;
		for(int i=0;i<ch.length;i++){
			Median mdan = mp.get(ch[i]);
			if(flush){
				mdan.flush();
			}
			int a = mdan.available();
			if(min > a) min = a;
		}
		for(int line = 0;line<min;line++){
			String out = timefield.getFirst();
			timefield.removeFirst();
			// チャネル順に出力
			for(int i=0;i<ch.length;i++){
				Median mdan = mp.get(ch[i]);
				double d = mdan.output();
				out += String.format(",%f",d);
			}
			fw.write(out+"\n");
		}
	}
	/**
	 * σをエクスポートする
	 */
	public void exportCSVSigmaall() {
		exportCSVSigmaall(",");
	}
	/**
	 * σをエクスポートする
	 * @param separator
	 */
	public void exportCSVSigmaall(String separator) {
		int lines = exportCSVSigma(DataBase.OldestTime,DataBase.LatestTime,separator);
		if(lines>0) JOptionPane.showMessageDialog(this,String.format("Export success %,d lines\n", lines)+DataBase.OldestTime.fmt()+"-"+DataBase.LatestTime.fmt()+"\n"+CSVSigmaOutputResultMessage);
	}
	/**
	 * σをエクスポートする
	 */
	public void exportCSVSigmadisplay() {
		wintime endtm = new wintime(GVDispTimeStart);
		endtm.inc_msec(1000*((int)(GVDispTimeRange_ms-1)/1000));
		int lines = exportCSVSigma(GVDispTimeStart,endtm,",");
		if(lines>0) JOptionPane.showMessageDialog(this,String.format("Export success %,d lines\n", lines)+DataBase.OldestTime.fmt()+"-"+DataBase.LatestTime.fmt()+"\n"+CSVSigmaOutputResultMessage);
	}
	//-----------------------------------------------------------------------------------------------------------------------
	/**
	 * 
	 */
	private int firstChannel(){
		return DataBase.firstChannel();
	}
	// ------------------------------------------
	// 拡大できる最大のスパンを計算する
	private int gerAvailableSpan(long center,long span){
		long up = center + span;
		long dn = center - span;
		if(up <= (long)Integer.MAX_VALUE && dn >=(long)Integer.MIN_VALUE){
			return (int)span;
		}
		if(up > (long)Integer.MAX_VALUE) up=(long)Integer.MAX_VALUE;
		if(dn < (long)Integer.MIN_VALUE) dn=(long)Integer.MIN_VALUE;
		if(up-center > center-dn) return (int)(center-dn);
		return (int)(up-center);
	}
	private ChannelDataCollection getChannelDataCollection(int ch){
		return DataBase.getChannelDataCollection(ch);
		//return getDataTreeMap().get(ch);
	}
	private ChannelOutlineDataCollection getChannelDataCollection60(int ch){
		return DataBase.getChannelDataCollection60(ch);
		//return getDataTreeMap().get(ch);
	}
	/**
	 * ｃｈ表示プロパティを取得する
	 * @param Point poi マウス位置（VIEW内座標）
	 * @return
	 */
	private ChannelDataDisplayProperty getChannelDataDisplayProperty(Point poi){
		int y = poi.y;
		
		if(GraphType == GRAPHIC_TYPE_NORMAL || GraphType== GRAPHIC_TYPE_NORMAL_OVERLAP ){ // 通常表示の場合
			if(GraphNum==0 ||AreaYaxis.height==0 ) return null;
			int index = y / (AreaYaxis.height / GraphNum);
			int dispIndex = 0;
			synchronized(DataBaseLock()){
				Iterator<Integer> ite = iteratorChannel();
				while(ite.hasNext()){
					int ch = ite.next();
					ChannelDataDisplayProperty pro = getChannelInfo(ch);
					if(pro != null){
						if(0 == pro.hide && !pro.Mask){
							if(index == dispIndex){
								return pro;
							}
							dispIndex++;
						}
					}
				}
			}
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
			// ドラムビューでHIDE THIS channel
			int ch = getChannelFromPointOnDrum(poi);
			if(ch != -1){
				return getChannelInfo(ch);
			}
			return null;
		}
		return null;
	}
	/**
	 * CHプロパティを得る
	 * @param ch chID
	 * @return
	 */
	private ChannelDataDisplayProperty getChannelInfo(int ch){
		ChannelDataDisplayProperty pro = ManualYScale.get(ch);
		ChannelDataDisplayProperty pro2 = ChConfig.getData(ch);
		if(pro2 != null && pro !=null){
			pro.hide = pro2.hide;
			pro.unitFactor = pro2.unitFactor;
			pro.unitString = pro2.unitString;
			pro.unitType = pro2.unitType;
			return pro;
		}
		else if(pro2 != null){
			return pro2;
		}
		return new ChannelDataDisplayProperty(ch);
	}
	public int getChannelMax(){ return ChannelMax; }
	// ------------------------------
	// すべてのＣＨの数を得る。
	private int getChannelNumber(){
		//return getChannel().size();
		return sizeChannel();
	}
	public int getColorCalcMode(){
		return ColorCalcMode;
	}
	public int getColorMode(){
		return ColorMode;
	}
	// --------------------------------------------------
	// 時間を指定してcdlit を得る
//	private ChannelData getDataFromTime(int ch,wintime tm){
//		ChannelDataCollection dcol = getChannelDataCollection(ch);
//		if(dcol==null) return null;
//		TreeMap<Integer,ChannelData> cdlist = dcol.getChannelData();
//		return cdlist.get(tm.getValueS());
//	}
	// ------------------------------
	// 表示されているＣＨの数を得る
	private int getDisplayChannel(){
		int ret = 0;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			for(int index=0;ite.hasNext();index++){
				int ch = ite.next();
				if(!isChannelHide(ch)){
					ret++;
				}
			}	
		}
		return ret;
	}
	public long getDispTimeRange_ms() {
		return GVDispTimeRange_ms;
	}
	// --------------------------------------------------
	// エクスポートするフォルダの読み出し
	public String getExportFolder(){
		return ExportFolder;
	}
	// ------------------------------------------
	// 最初に表示されているｃｈを見つける
	private int getFirstDisplayChannel(){
		int ret = -1;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null) continue;
				if(pro.hide == 0 && !pro.Mask) {
					return ch;
				}
			}
		}
		return ret;
	}
	/**
	 * マニュアルスケールダイアログから読み込む
	 */
	private void getFixScaleDialogData(){
		int cen = FixScaleDialog.CenterValue;
		long amp = FixScaleDialog.AmplitudeValue;
		int ch = FixScaleDialog.channel;
		int min = intSub((long)cen,(long)amp/2);
		int max = intAdd((long)cen,(long)amp/2);
		//ChannelDataDisplayProperty pro = new ChannelDataDisplayProperty(ch);
		ChannelDataDisplayProperty pro = getChannelInfo(ch);
		pro.setMaxMin(max, min);
		pro.offset = FixScaleDialog.OffsetValue;
		pro.mode = ChannelDataDisplayProperty.MODE_FIX;
		ManualYScale.put(ch, pro);
		ChConfig.setData(ch, pro);
		if(FixScaleDialog.Set2AllChannelAmp){
			doSingleScale((long)amp,pro);
		}
		if(FixScaleDialog.Set2AllChannelCenter){
			doSingleCenter((long)cen,pro);
		}
		
	}	
	private int getFontHeight(int point){
		return (int)(point * CurrentDPI /72.0f);
	}
	// 高さを与えて必要なフォントポイントを得る
	private int getFontPoint(int hight){
		return (int)(hight * 72.0f /CurrentDPI);
	}


	public int getGraphNum(){ return GraphNum; }
	// ------------------------------------------
	// 最後に表示されているｃｈを見つける
	private int getLastDisplayChannel(){
		int ret = -1;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null) continue;
				if(pro.hide == 0 && !pro.Mask) {
					ret=ch;
				}
			}
		}
		return ret;
	}
	// 最後に非表示されているｃｈを見つける
	private int getLastUnDisplayChannel(){
		int ret = -1;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null) continue;
				if(pro.hide == 1 && !pro.Mask) {
					ret=ch;
				}
			}
		}
		return ret;
	}
	// ------------------------------------------
	// １つ下のｃｈを見つける
	private int getNextChannel(int chtofind){
		int ret = -1;
		int flag = 0;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null) continue;
				if(pro.Mask)continue; 
				if(flag !=0){
					return ch;
				}
				if(ch==chtofind){
					flag = 1;
				}
			}
		}
		return ret;
	}
	// ------------------------------
	//　描画時間を得る
	public long getPaintTime(){
		return PaintTimeCurrent;
	}
	private JFrame getParentFrame(){
		if(pArentFrame==null){
			Component c0 = getParent();
			while(c0 != null){
				c0 = c0.getParent();
				if(c0 instanceof JFrame) break;
			}
			pArentFrame=(JFrame)c0;
		}
		return pArentFrame;
		//return (Frame)(this.getParent().getParent().getParent());
	}
	// ------------------------------
	// 自動スケール
	//public void AutoScale(){
	//	doAutoScale();
	//}
	private Frame getParentFrame2(){
		Container c = this.getParent();
		while(true){
			if(c instanceof Frame){
				return (Frame)c;
			}
			c = c.getParent();
			if(c==null) return null;
		}
	}
	private Point getParentPoint(){
		return getParentFrame().getLocationOnScreen();
		//return getParent().getParent().getParent().getLocation();
	}
	// ------------------------------------------
	// １つ上のｃｈを見つける
	private int getPrevChannel(int chtofind){
		int ret = -1;
	//	int flag = 0;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null) continue;
				if(pro.Mask)continue; 
				if(ch==chtofind){
					return ret;
				}
				ret = ch;
				
			}
		}
		return ret;
	}
	public winDataBase getwinDataBase(){
		return DataBase;
	}
	/**
	 * 指定領域の振幅最大値・最小値を返す
	 * @param ch
	 * @param start
	 * @param range_ms
	 * @return
	 */
	private Point getYMaxMin(int ch, wintime start,long range_ms){
		Point ret  = new Point(Integer.MIN_VALUE,Integer.MAX_VALUE);
		int loopcount = (int)((range_ms+999L)/1000L);
		int max = Integer.MIN_VALUE;
		int min = Integer.MAX_VALUE;
		
		if(loopcount>(60)){
			ChannelOutlineDataCollection dcol = getChannelDataCollection60(ch);
			if(dcol!=null) {
				wintime tm = new wintime(start.year(),start.month(),start.day(),start.hour(),start.min(),0,0);
				for(int i=0;i<loopcount/60;i++){
					ChannelOutlineData cd  = dcol.getChannelData(tm);
					if(cd!=null){
						if(max < cd.getMax()) max = cd.getMax();
						if(min > cd.getMin()) min = cd.getMin();
					}
					tm.inc_min();
				}
			}
		}
		if(max == Integer.MIN_VALUE || min == Integer.MAX_VALUE){
			ChannelDataCollection dcol = getChannelDataCollection(ch);
			if(dcol==null) {
				return ret;
			}
			TreeMap<Integer,ChannelData> cdlist = dcol.getChannelData();
			wintime tm = new wintime(start);
			for(int i=0;i<loopcount;i++){
				ChannelData cd  = cdlist.get(tm.getValueS());
				if(cd!=null){
					if(max < cd.getMax()) max = cd.getMax();
					if(min > cd.getMin()) min = cd.getMin();
				}
				tm.inc_sec();
			}
		}
		ret.x = max;
		ret.y = min;
		
		return ret;
	}
	// ------------------------------
	// すべてのＣＨを隠す
	private void hideAllChannel(int hidech) {
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro != null){
					if(pro.getChannel()!=hidech){
						pro.hide = 1;
						ManualYScale.put(ch,pro);
						ChConfig.setData(ch, pro);
					}
				}
			}
		}
		repaintAll();
	}
	// --------------------------------
	// すべての振幅を増やす/減らす
	public void incDecAmplitude(boolean inc){
		int mode = 0;
		if(!inc) mode = 1;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro != null){
					if(pro.hide == 0 && !pro.Mask){
						YAxisScaleSome(pro,mode);	
						pro.mode = ChannelDataDisplayProperty.MODE_FIX;
						ManualYScale.put(ch, pro);
						ChConfig.setData(ch, pro);
					}
				}
			}
		}
		repaintAll();
	}
	private void initFullScale() {
		Set<Integer> set = ChConfig.getChannelKeySet();
		Iterator<Integer> ite = set.iterator();
		while(ite.hasNext()){
			int ch = ite.next();
			ChannelDataDisplayProperty p = ChConfig.getData(ch);
			if(p!=null) getwinDataBase().setFullScale(ch,(int) p.FullScale);
		}
	}
	// ------------------------------------------
	// int を加える
	private int intAdd(long a, long b){
		long al = a;
		long bl = b;
		if(al+bl<=(long)Integer.MAX_VALUE) return (int)(al+bl);
		return Integer.MAX_VALUE;
	}
	// ------------------------------------------
	// int を減ずる
	private int intSub(long a, long b){
		long al = a;
		long bl = b;
		if(al-bl >= (long)Integer.MIN_VALUE) return (int)(al-bl);
		return Integer.MIN_VALUE;
	}
	public boolean isAutoTrack() {
		return AutoTrack;
	}
	// ------------------------------
	// ＣＨが隠されているかどうか
	private boolean isChannelHide(int ch){
		ChannelDataDisplayProperty pro = getChannelInfo(ch);
		if (pro != null) {
//			if (pro.hide != 0) {
//				return true;
//			}
//			if(pro.Mask) return true;
			if(pro.hide !=0 && !pro.Mask) return true;
		}
		return false;
	}
	
	
	public boolean isConvSigmaSNR() {
		return ConvSigmaSNR;
	}
	// ---------------------------------------
	// ドキュメントのアクセス
//	private TreeSet<Integer> getChannel(){
//		return DataBase.getChannel();
//	}
	private boolean isEmptyChannel(){
		return DataBase.isEmptyChannel();
	}
	// --------------------------------------------------
	// 
	//------------------------------
	// ｃｈ情報を表示するかどうか
	public boolean isShowChannelInfo() {
		return ShowChannelInfo;
	}
	@Override
	// 
	// アイテム状態の変化
	public void itemStateChanged(ItemEvent e) {
	    
	}
	private Iterator<Integer> iteratorChannel(){
		return DataBase.iteratorChannel();
	}
	/**
	 * プロパティのロードとセーブ
	 * @param save
	 */
	public void LoadSaveProperty(boolean save){
		if(save){
			winchkgProperty.TimeChartColor = getColorMode();
			
			winchkgProperty.ShowMean = isShowChannelInfo();
			winchkgProperty.ShowTimeSamplePoint = getShowTimeSamplePoint();
			winchkgProperty.ShowAmplitudeLabel = getAmpLabelEnable();
			winchkgProperty.ClipWave = ClipRect;
			winchkgProperty.ShowCursorTime = DrawCursorTimeInfo;
			
			winchkgProperty.ShowChannel = ShowChannelNumber;
			winchkgProperty.ShowStation = ShowStation;
			winchkgProperty.ShowComponent = ShowComponent;
			winchkgProperty.ShowAmplitudeLabelAxis = DrawYAxisLabel;
			winchkgProperty.GraphicX = GraphAreaXOffset;
			winchkgProperty.PhysicalQuantityFormat = getPhysicalQuantityFormat();
			winchkgProperty.TimeSeriesExportImageDir =  ExportImageDir;
			winchkgProperty.YAxisDrawCenter = YAxisProp.DrawCenter;
			winchkgProperty.YAxisDrawControlHandle = YAxisProp.DrawControlHandle;
			winchkgProperty.YAxisLabelFontMax = YAxisProp.LabelFontMax;
			winchkgProperty.YAxisLabelFontMin = YAxisProp.LabelFontMin;
			winchkgProperty.YAxisLabelNumberMax = YAxisProp.LabelNumberMax;
			winchkgProperty.XAxisDrawXBar = XAxisProp.DrawXBar;
			winchkgProperty.XAxisDrawClock = XAxisProp.DrawClock;

		}
		else{
			setColorMode(winchkgProperty.TimeChartColor);
			setShowChannelInfo(winchkgProperty.ShowMean);
			setShowTimeSamplePoint(winchkgProperty.ShowTimeSamplePoint);
			setAmpLabelEnable(winchkgProperty.ShowAmplitudeLabel);
			ClipRect = winchkgProperty.ClipWave;
			DrawCursorTimeInfo = winchkgProperty.ShowCursorTime;
			ShowChannelNumber = winchkgProperty.ShowChannel;
			ShowStation = winchkgProperty.ShowStation;
			ShowComponent = winchkgProperty.ShowComponent;
			DrawYAxisLabel = winchkgProperty.ShowAmplitudeLabelAxis;
			GraphAreaXOffset = winchkgProperty.GraphicX;
			setPhysicalQuantityFormat(winchkgProperty.PhysicalQuantityFormat); 
			ExportImageDir = winchkgProperty.TimeSeriesExportImageDir;
			YAxisProp.DrawCenter = winchkgProperty.YAxisDrawCenter;
			YAxisProp.DrawControlHandle = winchkgProperty.YAxisDrawControlHandle;
			YAxisProp.LabelFontMax = winchkgProperty.YAxisLabelFontMax;
			YAxisProp.LabelFontMin = winchkgProperty.YAxisLabelFontMin;
			YAxisProp.LabelNumberMax = winchkgProperty.YAxisLabelNumberMax;
			XAxisProp.DrawXBar = winchkgProperty.XAxisDrawXBar;
			XAxisProp.DrawClock = winchkgProperty.XAxisDrawClock;
		}

	}
	// タイムマーカーを追加
	private void doTimeMakerAdd(int idx){
		getDialogTimeMarker().addTime(CurrentMouseTime,idx);
		//System.out.println("MakerAdd" +CurrentMouseTime.fmt() );
	}
	// マウス位置が変化したとき
	private int onMousePositionUpdate(int x,int y){
		boolean xchg=false;
		boolean ychg=false;
		if(CurrentMousePositon.x != x) xchg=true;
		if(CurrentMousePositon.y != y) ychg=true;
		CurrentMousePositon.setLocation(x,y);
		if(xchg){
			calcCurrentCursorTime();
		}
		int ret=0;
		if(xchg) ret |= 1;
		if(ychg) ret |= 2;
		return ret;
	}
	@Override
	/**
	 * マウスクリック時
	 */
	public void mouseClicked(MouseEvent e) {
		int mod = e.getModifiers();
		boolean ctrlKey = (ActionEvent.CTRL_MASK & mod)!=0;
		boolean shiftKey = (ActionEvent.SHIFT_MASK & mod)!=0;
		
		
		if(GraphType == GRAPHIC_TYPE_NORMAL||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
			// 右ボタン
			if (SwingUtilities.isRightMouseButton(e)) {
				CurrentPopupPosition.x = e.getX();
				CurrentPopupPosition.y = e.getY();
				if(AreaXaxis.contains(e.getX(), e.getY())){
					PopupMenuTime.show(e.getComponent(), e.getX(), e.getY());
				}
				else if(AreaYaxis.contains(e.getX(), e.getY())){
					PopupMenuAmp.show(e.getComponent(), e.getX(), e.getY());
				}
				else if(AreaGraph.contains(e.getX(),e.getY())){ // グラフエリア
					doZoomOut();
				}
			}
			else if(SwingUtilities.isLeftMouseButton(e)){
				if( XGuide.contains(e.getX(),e.getY())){
					double ratio = (double)(e.getX() - XGuide.x)/XGuide.width;
					doSetTime(ratio);
				}
				else if(DebugConsole.contains(e.getX(),e.getY())){
					if(Console!=null){
						Console.setVisible(true);
					}
				}
				else if(AreaGraph.contains(e.getX(),e.getY())){ // グラフエリア
					if(ctrlKey) {
						if(shiftKey) doTimeMakerAdd(0);
						else         doTimeMakerAdd(-1);
						repaintAll();
					}
					else doZoomIn();
				}
				else{
					synchronized(DataBaseLock()){
						Iterator<Integer> ite = iteratorChannel();
						while(ite.hasNext()){
							int ch = ite.next();
							ChannelDataDisplayProperty pro = getChannelInfo(ch);
							if(pro != null){
								if(pro.hide == 0 && !pro.Mask){
									if(pro.AxisShiftUp.contains(e.getX(),e.getY())){
										YAxisShiftSome(pro,1);
										pro.mode = ChannelDataDisplayProperty.MODE_FIX;
										ManualYScale.put(ch, pro);
										ChConfig.setData(ch, pro);
										repaintAll();
									}
									else if(pro.AxisShiftDown.contains(e.getX(),e.getY())){
										YAxisShiftSome(pro,0);
										pro.mode = ChannelDataDisplayProperty.MODE_FIX;
										ManualYScale.put(ch, pro);
										ChConfig.setData(ch, pro);
										repaintAll();
									}
									else if(pro.YAxisScaleUp.contains(e.getX(),e.getY())){
										YAxisScaleSome(pro,1);	
										pro.mode = ChannelDataDisplayProperty.MODE_FIX;
										ManualYScale.put(ch, pro);
										ChConfig.setData(ch, pro);
										repaintAll();
									}
									else if(pro.YAxisScaleDown.contains(e.getX(),e.getY())){
										YAxisScaleSome(pro,0);	
										pro.mode = ChannelDataDisplayProperty.MODE_FIX;
										ManualYScale.put(ch, pro);
										ChConfig.setData(ch, pro);
										repaintAll();
									}
								}
								if(pro.ChannelTitle.contains(e.getX(),e.getY())){
	//								if(pro.hide == 0) pro.hide = 1;
	//								else				pro.hide = 0;
	//								ManualYScale.put(ch,pro);
	//								ChConfig.setData(ch, pro);
	//								repaintAll();
								}
							}
						}
					}
				}
			}
		}
		else if(GraphType == GRAPHIC_TYPE_DRUMRECORDER){
			if (SwingUtilities.isRightMouseButton(e)) {
				CurrentPopupPosition.x = e.getX();
				CurrentPopupPosition.y = e.getY();
				PopupMenuDrum.show(e.getComponent(), e.getX(), e.getY());
			}
			else if(SwingUtilities.isLeftMouseButton(e)){
			}
		}
	}
	
	@Override
	/**
	 * マウスドラッグ中
	 */
	public void mouseDragged(MouseEvent arg0) {

		//int btn = arg0.getButton();
		//if(MouseEvent.BUTTON1 != btn) return;
		int x=arg0.getX();
		int y=arg0.getY();
		onMousePositionUpdate(x,y);
		if(GraphType == GRAPHIC_TYPE_DRUMRECORDER) return;
		if(AreaGraph.contains(arg0.getX(),arg0.getY())){
			switch(TimeSpanDraggPhase){
			case 1:// No dragg
				DraggStart.setLocation(arg0.getX(),arg0.getY());
				TimeSpanDraggPhase = 2;
				break;
			case 2:// Dragging
				DraggEnd.setLocation(arg0.getX(),arg0.getY());
				repaintAll();
				break;
			case 3:// Drag finished
				break;
			}
		}
//		else if(AreaGraphHandle.contains(arg0.getX(),arg0.getY())){
//			AreaGraphDraggingMode=1;
//		}
//		else if(AreaXaxis.contains(arg0.getX(),arg0.getY())){
//			AreaGraphDraggingMode=1;
//		}
		else{
			if(TimeMarkerDraggPhase==0){
				Iterator<TimeMarker> ite = TmMarker.iterator();
				while(ite.hasNext()){
					TimeMarker tm = ite.next();
					Rectangle r = tm.IconRect;
					if(r.contains(x,y)){
						TimeMarkerDraggPhase=1;
						CurTimeMarker = tm;
						break;
					}
				}
			}
		}
		if(TimeMarkerDraggPhase>0){
			//System.out.println("TimeMarkerDrag");
			doTimeMakerAdd(CurTimeMarker.Number);
			repaintAll();
		}
		if(AreaGraphDraggingMode>0){
			int deltax = x - mOusePressPoint.x;
			GraphAreaXOffset = xBoundOrg+deltax; 
			repaintAll();
		}
		if(AreaTimeDraggingMode>0){
			int deltax = x - mOusePressPoint.x;
			//wintime t2 = x2time(xstart-x1+width,x2-x1,	DispTimeStart,	DispTimeRange_ms);
			long offsetms = (long)((float)GVDispTimeRange_ms*(float)deltax/(float)AreaGraph.width);
			offsetms *= -1;
			if(offsetms>0){
				wintime wt = new wintime(dIspStartTimeOrg);
				wt.inc_msec(offsetms);
				GVDispTimeStart=wt;
				onDispTimeChange();
				repaintAll();
			}
			else if(offsetms<0){
				wintime wt = new wintime(dIspStartTimeOrg);
				wt.dec_msec(-offsetms);
				GVDispTimeStart=wt;
				onDispTimeChange();
				repaintAll();
			}
		}
		if(xGuideViewRangeDraggingMode>0){
			int deltax = x - mOusePressPoint.x;
			long offsetms = (long)((float)deltax * xGuideFactorms);
			if(offsetms>0){
				wintime wt = new wintime(dIspStartTimeOrg);
				wt.inc_msec(offsetms);
				GVDispTimeStart=wt;
				onDispTimeChange();
				repaintAll();
			}
			else if(offsetms<0){
				wintime wt = new wintime(dIspStartTimeOrg);
				wt.dec_msec(-offsetms);
				GVDispTimeStart=wt;
				onDispTimeChange();
				repaintAll();
			}
		}
		if(AreaYaxisDraggingMode>0){
			repaintAll();
		}
	}
	@Override
	public void mouseEntered(MouseEvent e) {
		requestFocusInWindow();
	}
	@Override
	public void mouseExited(MouseEvent arg0) {
		if(GraphType == GRAPHIC_TYPE_DRUMRECORDER) return;
		CursorDisplay = 0;
		repaintAll();
	}
	
	@Override
	/**
	 * マウス移動
	 */
	public void mouseMoved(MouseEvent e) {
		int x=e.getX();
		int y=e.getY();
		int cdorg = CursorDisplay;
		int chg = onMousePositionUpdate(x,y);
		if(GraphType == GRAPHIC_TYPE_DRUMRECORDER) {
			if(chg!=0){
				if(CursorDisplay!=0){
					repaintAll();
				}
			}
			if(cdorg != CursorDisplay && CursorDisplay ==0){
				repaintAll();
			}
		}
		else if(GraphType == GRAPHIC_TYPE_NORMAL ||GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP ){
			if(AreaGraph.contains(x,y)){
				CursorDisplay = 1;
				if(chg!=0){
					repaintAll();
				}
			}
			else if(AreaYaxis.contains(x,y)){
				CursorDisplay=0;
				if(chg!=0){
					repaintAll();
				}
			}
			else{
				if(CursorDisplay != 0) {
					//CursorOut = true;
					CursorDisplay = 0;
					repaintAll();
				}
			}
		}
	}
	private int xBoundOrg;
	private wintime dIspStartTimeOrg=new wintime();
	private Point mOusePressPoint = new Point();
	private int AreaGraphDraggingMode=0;
	private int AreaTimeDraggingMode=0;
	private int xGuideViewRangeDraggingMode=0;
	private Cursor OriginalCursor;
	private int AreaYaxisDraggingMode=0;
	private int YAxisDraggOrigGraphIndex;
	private int AreaYaxisSelected=0;
	@Override
	/**
	 * マウスプレス
	 */
	public void mousePressed(MouseEvent arg0) {
		if(GraphType == GRAPHIC_TYPE_DRUMRECORDER) return;
		int x=arg0.getX();
		int y=arg0.getY();
		mOusePressPoint.x=x;
		mOusePressPoint.y=y;
		AreaYaxisSelected = 0;
		if(AreaGraph.contains(x,y)){
			TimeSpanDraggPhase = 1;
		}
		if(AreaGraphHandle.contains(x,y)){
			xBoundOrg = GraphAreaXOffset; 
			AreaGraphDraggingMode = 1;
		}
		if(AreaTimeAxis.contains(x,y)){
			dIspStartTimeOrg=GVDispTimeStart;
			AreaTimeDraggingMode=1;
		}
		if(XGuideViewRange.contains(x,y)){
			dIspStartTimeOrg=GVDispTimeStart;
			xGuideViewRangeDraggingMode=1;
		}
		// Ｙ軸エリア
		if(AreaYaxis.contains(x,y)){
			if(GraphType == GRAPHIC_TYPE_NORMAL){
				int idx = getYaxisIndex(y);
				if(idx>=0){
					YAxisDraggOrigGraphIndex = idx;
					OriginalCursor = getCursor();
					setCursor(new Cursor(Cursor.HAND_CURSOR));
					AreaYaxisDraggingMode=1;
					AreaYaxisSelected = 1;
					repaintAll();
				}
			}
		}
	}
	
	/**
	 * ポイントを与えて上から数えて何番目のグラフかを返す。（０ベース）
	 * 範囲外はマイナスを返す。
	 * @param 
	 * @return
	 */
	private int getYaxisIndex(int py){
		int y0 = AreaYaxis.y;
		int yh = AreaYaxis.height;
		
		int y = py-y0;
		if(y<=0 || y>=yh) return -1;
		
		if(GraphNum<1) return -1;
		if(GraphNum==1 || GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP) return 0;
		
		int gh = (int)GraphHeight;
		int idx;
		for(idx=0;idx<GraphNum;idx++){
			if(y >= idx*gh && y < (idx+1)*gh){
				break;
			}
		}
		return idx;
	}
	private Point mOuseReleasedPoint = new Point();
	//private boolean IsDrawClock=true;
	//private boolean IsDrawXGuide=true;
	@Override
	/**
	 * マウスリリース
	 */
	public void mouseReleased(MouseEvent e) {
		if(GraphType == GRAPHIC_TYPE_DRUMRECORDER ) return;
		setCursor(OriginalCursor);
		int x=e.getX();
		int y=e.getY();
		mOuseReleasedPoint.x=x;
		mOuseReleasedPoint.y=y;
		if(AreaGraph.contains(x,y)){
			if(TimeSpanDraggPhase==2){
				TimeSpanDraggPhase = 3;
				repaintAll();
			//System.out.println("dragFinished");
			}
			else{
				TimeSpanDraggPhase = 0;
			}
		}
		if(TimeMarkerDraggPhase!=0){
			Rectangle r = CurTimeMarker.IconRect;
			r.x=x;
			r.width = 10;
			TimeMarkerDraggPhase=0;
			CurTimeMarker=null;
		}
		if(AreaYaxisDraggingMode!=0){
			if(AreaYaxis.contains(x,y)){
				int idx = getYaxisIndex(y);
				if(idx != YAxisDraggOrigGraphIndex){
					// YAxisDraggOrigGraphIndex番目のＣＨをidx番目に移動する
					moveDisplayChannelPosition(YAxisDraggOrigGraphIndex,idx);
				}
			}
		}
		AreaYaxisDraggingMode=0;
		AreaGraphDraggingMode=0;
		AreaTimeDraggingMode=0;
		xGuideViewRangeDraggingMode=0;
	}
	/**
	 * idxsor番目のＣＨをidxdest番目に移動する
	 * @param yAxisDraggOrigGraphIndex2
	 * @param idx
	 */
	private void moveDisplayChannelPosition(int idxsor,int idxdes) {
		// System.out.println(String.format("Drop channel : %d to %d", idxsor,idxdes));
		ArrayList<Integer> chlist =  getDisplayChannelList();
		int chsor = chlist.get(idxsor);
		int chdes = chlist.get(idxdes);
		DataBase.moveChannel(chsor,chdes);
		repaintAll();

	}
	//-------------------------
	// チャンネル数、表示数が変わったとき
	private void onChangeDisplayChannel(){
		ParentPanel.onChangeDisplayChannel();
	}
	// --------------------------------------------------
	// コマンドインターフェイス
	@Override
	public void OnCommand(String cmd){
		String[] cm = Optionconsole.getCommand(cmd);
		if(cm==null) return;
		if(Console==null) return;
		if(0==cm[0].compareTo("help")){
			Console.print("**** Commmand summary ****\n");
			Console.print("help           : show this help\n");
			Console.print("disptime       : display time and date infomation \n");
			Console.print("dispch         : display channel infomation \n");
			Console.print("setcolormode mode : set color mode (mode=0-3)\n");
			Console.print("setcolor  brightness  : set color brightness (0(brighter) - 4(darker)) \n");
			Console.print("export         : export to CSV \n");
			Console.print("drawmean       : draw mean value only ON/OFF \n");
			Console.print("csvformat      : CSV format header and time ON/OFF \n");
			Console.print("winfiledatabase: Show win file data base info \n");
			Console.print("yaxis: set yaxis property \n");
				
		}
		else if(0==cm[0].compareTo("yaxis")){
			yAxisConf();
		}
		else if(0==cm[0].compareTo("disptime")){
			Console.print("Data "+DataBase.OldestTime.fmt()+"..." +DataBase.LatestTime.fmt()+"\n");
			wintime tm = new wintime(GVDispTimeStart);
			tm.inc_msec((int)GVDispTimeRange_ms);
			Console.print("Disp "+GVDispTimeStart.fmt()+"..." +tm.fmt()+"\n");
		}
		else if(0==cm[0].compareTo("dispch")){
			String mes = String.format("channel max=%d\n", getChannelNumber());
			Console.print(mes);
		}
		else if(0==cm[0].compareTo("setcolormode")){
			if(cm.length<2){
				Console.print(String.format("ColorMode=%d\n",ColorMode));
				return;
			}
			int mode = Integer.parseInt(cm[1]);
			setColorMode(mode);
			repaintAll();
		}
		else if(0==cm[0].compareTo("setcolor")){
			if(cm.length<2){
				Console.print(String.format("Color Bright=%d\n",ColorBrightness));
				return;
			}
			int bri = Integer.parseInt(cm[1]);
			ColorBrightness = bri;
			Console.print(String.format("Bright=%d\n",ColorBrightness));
			repaintAll();
		}
		else if(0==cm[0].compareTo("export")){
			exportCSVall();
		}
		else if(0==cm[0].compareTo("drawmean")){
			if(DrawMean==true) {
				DrawMean=false;
				repaintAll();
				Console.print("drawmean off\n");
			}
			else{
				DrawMean=true;
				repaintAll();
				Console.print("drawmean on\n");
			}
		}
		else if(0==cm[0].compareTo("csvformat")){
			if(CsvDispTime){
				CsvDispTime = false;
				CsvDispHeader = false;
				Console.print("CSV disp time=off, disp header=off\n");
			}
			else{
				CsvDispTime = true;
				CsvDispHeader = true;
				Console.print("CSV disp time=on, disp header=on\n");
			}
			
		}
		else if(0==cm[0].compareTo("winfiledatabase")){
			DataBase.showWinFileDataBase();
			Console.print("See std out!\n");
		}
		else {
			Console.print("???-> " + cm[0] + "\n");
		}
	

	}
	/**
	 * YAxisプロパティを編集する。
	 */
	@SuppressWarnings("deprecation")
	public void yAxisConf() {
		DlgXYAxisDrawProperty p = new DlgXYAxisDrawProperty();
		p.setData(YAxisProp);
		p.setData(XAxisProp);
		p.move(this.getLocationOnScreen().x, this.getLocationOnScreen().y);
		p.setVisible(true);
		if(p.Ok){
			p.getData(YAxisProp);
			p.getData(XAxisProp);
			repaintAll();
		}
	}
	/**
	 * // Y軸 拡大縮小、シフトのハンドル（ボタン）を描くかどうか
	 */
	public void setYaxisControlHandle(boolean draw){
		YAxisProp.DrawControlHandle=draw; // Y軸 拡大縮小、シフトのハンドル（ボタン）を描くかどうか
	}
	public boolean isYaxisControlHandle(){
		return YAxisProp.DrawControlHandle; // Y軸 拡大縮小、シフトのハンドル（ボタン）を描くかどうか
	}
	/**
	 * クロックを描くかどうか
	 * @return
	 */
	public boolean isIsDrawClock() {
		return XAxisProp.DrawClock;
	}
	public void setIsDrawClock(boolean isDrawClock) {
		XAxisProp.DrawClock = isDrawClock;
	}
	/**
	 * x軸ハンドルを書くかどうか
	 * @return
	 */
	public boolean isIsDrawXGuide() {
		return XAxisProp.DrawXBar;
	}
	public void setIsDrawXGuide(boolean isDrawXGuide) {
		XAxisProp.DrawXBar = isDrawXGuide;
	}
	///////////////////////////////////////////////////////
	private void onDispTimeChange(){
		//setCursor((Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR )));
		boolean reload = true;
		//if(DrawMode>1)	reload = false;
		if(DrawMode>=1)	reload = false;
		DataBase.setDispRange(GVDispTimeStart, (long)GVDispTimeRange_ms/1000,reload);
		ParentPanel.onChangeTimeStart(GVDispTimeStart);
	}
	@Override
	public void onFinishAddCh(boolean success) {
		
		
	}
	// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.
	// ファイルオープン
	public void openFiles(TreeSet<String> ts) {
		if(ts==null) return;
		addChData(ts);
        repaintAll();
        DataBase.setDispRange(GVDispTimeStart, (long)GVDispTimeRange_ms/1000,false);
        doChannelMax();
        doAutoScale();
	}
	/**
	 * 
	 * @param gd
	 * @param r
	 */
	private void drawHandle(Graphics gd,Rectangle r){
		
		if(IsPrint) return;
		int [] xp = new int [3];
		int [] yp = new int [3];
		xp[0] = r.x ; 			yp[0]=r.y;
		xp[1] = r.x; 			yp[1]=r.y+r.height-1;
		xp[2] = r.x+ r.width-1; yp[2]=r.y+r.height-1;
		gd.fillPolygon(xp,yp,3);
	}
	// ------------------------------------------
	// 描画の入り口
	public void paintComponent(Graphics g){
		Dimension dm = this.getSize();
		if(GraphType== GRAPHIC_TYPE_NORMAL || GraphType== GRAPHIC_TYPE_NORMAL_OVERLAP){
			paintComponetSubNomal(g,dm);
		}
		else{
			paintComponetSubDrum(g,dm);
		}
	}
	// ---------------------------------------------------------------------------------------------
	/**
	 * 通常モード描画 メイン
	 * @param g
	 * @param dm
	 */
	private void paintComponetSubNomal(Graphics g,Dimension dm){
		long painttime = System.currentTimeMillis();
//		if((painttime - PaintTimeStart < 50)&&!CursorOut) {
//			return;
//		}
		PaintTimeStart = painttime;
		super.paintComponents(g);	
		//Dimension dm = this.getSize();
		Graphics2D gd = (Graphics2D)g;
		gd.setBackground(ColorBackGround);
		gd.clearRect(0, 0, dm.width, dm.height);
		
		double xmax = (double)dm.width;
		double ymax = (double)dm.height;
		//int xbound = 250;	// 左から250まで
		int ybound = (int)(ymax-XaxisAreaHeight-XguideAreaHeight);	// 
		//AreaMisc = new Rectangle(0,ybound,xbound,(int)ymax-ybound);
		
		// キーボードフォーカスがあることを示すためにちょっと描画する
		if(!IsPrint){
			if(hasKeyFocus()){
				gd.setColor(Color.gray);
				int x1 = 2;
				int x2 = 2+8;
				int xc = (x1+x2)/2;
				int y1 = dm.height-3-8;
				int y2 = dm.height-3;
				int yc = (y1+y2)/2;
				switch(DrawCounter&0x3){
				case 0: gd.drawLine(x1,y1,x2,y2); break; 
				case 1: gd.drawLine(xc,y1,xc,y2); break; 
				case 2: gd.drawLine(x2,y1,x1,y2); break; 
				case 3: gd.drawLine(x1,yc,x2,yc); break; 
				}
				DrawCounter++;
			}
			drawMisc();
		}
		AreaYaxisSelected=0;
		// 表示するｃｈ数を得る
		int chnum = getChannelNumber();
		ChannelMax = chnum;
		if(chnum <= 0){
			Graphics2D gd2 = (Graphics2D)g;
			gd2.setColor(Color.gray);
			Font fon = g.getFont();
			gd2.setFont(fon.deriveFont(20f));
			drawTools.drawText(gd2, "Drop win files here" , dm.width/2-20, dm.height/2, drawTools.POSITION_CENTER_BOTTOM);
			gd2.setFont(fon.deriveFont(14f));
			if(GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
				drawTools.drawText(gd2, "overlapped mode" , dm.width/2-20, dm.height/2+30, drawTools.POSITION_CENTER_BOTTOM);
			}
			else{
				drawTools.drawText(gd2, "normal mode" , dm.width/2-20, dm.height/2+30, drawTools.POSITION_CENTER_BOTTOM);
			}
			return;
		}
		// 表示するｃｈの数を決定する
		int nnn = getDisplayChannel();
		if(nnn<=0){
			doChannelInc();
			nnn = getDisplayChannel();
		}
		if(nnn>CH_DISPLAY_MAX) nnn=CH_DISPLAY_MAX;
		if(GraphNum != nnn){
			GraphNum = nnn;
			ChannelNumChangeFlag=true;
		}
		
		//　表示する・しない場合のグラフ領域の高さ
		int ysizeOfNonDisplay1 = 0;	// 非表示のときの表示分
		int ysizeOfDisplay = ybound;
		float ysizeOfDisplay1 = 0;;
		if(GraphNum != 0) {
			if(GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
				ysizeOfDisplay1 = (float)ysizeOfDisplay;
			}
			else{
				ysizeOfDisplay1 = (float)ysizeOfDisplay/GraphNum;
			}
		}
		
		GraphHeight = ysizeOfDisplay1; // グラフ１つあたりの高さ
		if(GraphHeight < 8) ClipRect = false;
		ChFontDisplay = getFontPoint((int)(ysizeOfDisplay1/4));
		if(ChFontDisplay>16) ChFontDisplay=16;
		
		int xbound;
		if(GraphHeight > 16) {
			DispChannelNumber=true;
			//DispMisc = true;
			if(DrawYLabel){
				xbound= GraphAreaXOffset;	// 左から250まで
			}
			else{
				xbound= GraphAreaXOffset-100;	// 
			}
		}
		else{
			//DispMisc = false;
			DispChannelNumber=false;
			xbound= 4;
		}
		
		
		
		AreaMisc = new Rectangle(0,ybound,xbound,(int)ymax-ybound);
		
		int mc = (int)((float)GraphNum * (float)(CurrentMousePositon.y) / (float)ybound);
		if(mc>=0 && mc<GraphNum){
			CurrentMouseChannelIndex = mc;
		}

		int yspace = 5;
		// 各グラフエリアの座標を計算しておく
		int displaycounter = 0;
		float yposition = 0;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite22 = iteratorChannel();
			while(ite22.hasNext()){
				int ch = ite22.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null){
					pro = new ChannelDataDisplayProperty(ch);
				}
				if(pro.hide==0 && !pro.Mask){
					displaycounter++;
					if(displaycounter>CH_DISPLAY_MAX){
						pro.hide = 1;
					}
				}
				float ydelta = ysizeOfNonDisplay1;
				if(pro.hide == 0 && !pro.Mask){
					ydelta = ysizeOfDisplay1;
				}
				if(ydelta >0f && ydelta <= yspace*2){
					yspace = 0;
				}
				// CH間にすきまを作る
				pro.ChannelArea = new Rectangle(0,(int)yposition+yspace,xbound,(int)ydelta-yspace*2);
				
				int yheight;
				if(ydelta>15) yheight = ((int)(ydelta-10)/5);  // ボタンの最小の高さ＝１；
				else          yheight = (int)(ydelta / 4);		// ボタンの標準高さ＝1/4
				
				
				
				if(yheight>16) yheight=16; // ボタンの最大の高さ
				pro.AxisShiftUp   = new Rectangle(xbound-9-4,(int)yposition + 6 + yspace ,								9,	yheight);
				pro.YAxisScaleUp  = new Rectangle(xbound-9-4,(int)yposition + (int)(ydelta/2) - yheight-4 + yspace,		9,	yheight);
				pro.YAxisScaleDown= new Rectangle(xbound-9-4,(int)yposition + (int)(ydelta/2) +4  + yspace,				9,	yheight);
				pro.AxisShiftDown = new Rectangle(xbound-9-4,(int)yposition + (int)ydelta - yheight-6 + yspace,			9,	yheight);
				
				if(pro.hide == 0 && !pro.Mask) 	pro.ChannelTitle = new Rectangle(0,(int)yposition,100,20);
				else  							pro.ChannelTitle = new Rectangle(0,(int)yposition,0,0);
				
				if(GraphType == GRAPHIC_TYPE_NORMAL){ // 重ね書きなら加えない
					yposition += ydelta;
				}
				ManualYScale.put(ch, pro);
				ChConfig.setData(ch, pro);
				//pro.setColorSaturateBright(ColorSaturate, ColorBrightness);
				pro.setColorBrightness(ColorBrightness);
			}
		}
		
		
		// ｘ軸（時間軸）を描画
		drawXaxis2(gd, xbound, ybound,(int)xmax,(int)(ybound+XaxisAreaHeight-4),4);
		// ガイドを表示
		if(!IsPrint && XAxisProp.DrawXBar){
			drawXGuide(gd, xbound+((int)xmax-xbound)*3/10,ybound+XaxisAreaHeight,xbound+((int)xmax-xbound)*7/10,ybound+XaxisAreaHeight+XguideAreaHeight);
		}
		// ｘ軸の領域を保存
		AreaXaxis = new Rectangle(xbound, ybound,(int)xmax-1-xbound,(int)ymax-1-ybound);
		AreaTimeAxis = new Rectangle(xbound, ybound+10,(int)xmax-1-xbound,(int)(ymax-1-ybound)*2/3);
		// Ｙ軸の領域を保存
		AreaYaxis = new Rectangle(0, 0,xbound-1,ybound-1);
		// グラフ領域を保存
		//AreaGraph = new Rectangle(xbound, 0,(int)xmax-1,ybound-1);
		AreaGraph = new Rectangle(xbound, 0,(int)xmax-1-xbound,ybound-1);
		// Graphエリアのサイズを変更するハンドル
		AreaGraphHandle = new Rectangle(xbound-20,ybound+1,8,8);
		drawHandle(gd,AreaGraphHandle);
		
		
		DebugConsole = new Rectangle((int)xmax-20,(int)ymax-20,(int)xmax,(int)ymax);
		
		// １グラフの高さ
		// 表示されている最小値、最大値
		int[] yaxismax = new int[chnum+20];
		int[] yaxismin = new int[chnum+20];
		int[] yaxiscen = new int[chnum+20];
		long[] yaxisamp = new long[chnum+20];
		DataChannel = new int[chnum+20];
		Datamean = new double[chnum+20];
		Datavariant = new double[chnum+20];
		DataSampleRate = new int[chnum+20];
		DisplayChannel = new int[chnum+20];
		DisplayChannelNumber=0;
		
		//double[] datavariance = new double[chnum+20];
		
		int yxmax = Integer.MIN_VALUE;
		int yxmin = Integer.MAX_VALUE;
		

		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			int dispindex=0;
			for(int index2=0;ite.hasNext();index2++){
				int ch = ite.next();
				
				ChannelDataDisplayProperty pro2 = getChannelInfo(ch);
				if(isChannelHide(ch)){
					continue;
				}
				if(dispindex == CurrentMouseChannelIndex){
					CurrentMouseChannel = ch;
				}
				//if(CursorDisplay ==0) CurrentMouseChannel = -1;
				// CH番号を表示
				if(GraphType == GRAPHIC_TYPE_NORMAL){ 
					drawChannelNumber(gd, 0,0+pro2.ChannelArea.y,xbound,pro2.ChannelArea.y+pro2.ChannelArea.height,4,ch,ChFontDisplay,pro2,null);
				}
				else{ // 重ね書きの場合、チャネル番号を少しずらして書く
					float height =  (float)ysizeOfDisplay/GraphNum;
					int offset = (int)(dispindex*height);
					//if(pro2.ChannelArea.y + offset + height <= pro2.ChannelArea.height){
						pro2.setColorCalcMode(ColorCalcMode); // 色の計算モード
						Color dataCol = pro2.getDataColor();
						if(ColorMode == COLOR_BW_BLACK_BACK || ColorMode == COLOR_BW_WHITE_BACK) dataCol = ColorDataBW; 
						drawChannelNumber(gd, 0,0+pro2.ChannelArea.y + offset,xbound,pro2.ChannelArea.y+pro2.ChannelArea.height,4,ch,12f,pro2,dataCol);
					//}
				}
				dispindex++;
				Point maxmin = getYMaxMin(ch,GVDispTimeStart,(int)GVDispTimeRange_ms);
				int max = maxmin.x;
				int min = maxmin.y;
				Datavariant[index2] = 0.0;
				yaxismax[index2] = max;
				yaxismin[index2] = min;
				if(!(max==Integer.MIN_VALUE || min==Integer.MAX_VALUE)){
					if(yxmax < max) yxmax = max;
					if(yxmin > min) yxmin = min;
					yaxiscen[index2] = (int)(((long)max + (long)min)/2L);
					yaxisamp[index2] = (long)max - (long)min;
				}
				DisplayChannel[DisplayChannelNumber] = index2;
				DisplayChannelNumber++;
			}
		}
		// 全表示ＣＨのＹ最大値
		long ampmax = 0;
		for(int index=0;index<yaxismax.length;index++){
			if(yaxisamp[index] > ampmax) ampmax = yaxisamp[index];
		} 
		// 全体をクリップしとく
		gd.clipRect(0,0,(int)xmax,ybound+10);
		
		long drawData2MaxSpan = DrawData2MaxSpan;
		DrawData2MaxSpan = Integer.MIN_VALUE;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite3 = iteratorChannel();
			int dispIndex = 0;
			DrawPoint =0;
			for(int index=0;ite3.hasNext();index++){
				int ch = ite3.next();
				if(isChannelHide(ch)){
					continue;
				}
				
				ChannelDataCollection dcol = getChannelDataCollection(ch);
				if(dcol==null) continue;
				assert(dcol!=null);
				TreeMap<Integer,ChannelData> cdlist = dcol.getChannelData();
				int max = yaxismax[index];
				int min = yaxismin[index];
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro != null){
					// Ｓｉｎｇｌｅ　Scaleを実行するところ
					if(pro.mode == ChannelDataDisplayProperty.MODE_FIT_SCALE){ // fit scale all channel max
						int maxdisp = (int)pro.getMaxDisp();
						int mindisp = (int)pro.getMinDisp();
						if(maxdisp!=Integer.MIN_VALUE && mindisp!=Integer.MAX_VALUE && drawData2MaxSpan != Integer.MIN_VALUE){
							ampmax = drawData2MaxSpan;
							long cen = ((long)maxdisp+(long)mindisp)/2L;
							max = intAdd(cen,(ampmax)/2);
							min = intSub(cen,(ampmax)/2);
						}
						else{
							max = intAdd(yaxiscen[index],(ampmax)/2);
							min = intSub(yaxiscen[index],(ampmax)/2);
						}
						pro.mode = ChannelDataDisplayProperty.MODE_FIX;
					}
					else if(pro.mode==ChannelDataDisplayProperty.MODE_FIX){ //  manual scale (FIX)
						max = (int)pro.getMax();
						min = (int)pro.getMin();
					}
					else if(pro.mode==ChannelDataDisplayProperty.MODE_AUTO){ // auto scale (FIX scale once)
						if(max!=Integer.MIN_VALUE && min != Integer.MAX_VALUE){
							int maxdisp = (int)pro.getMaxDisp();
							int mindisp = (int)pro.getMinDisp();
							if(maxdisp!=Integer.MIN_VALUE && mindisp!=Integer.MAX_VALUE){
								max  = maxdisp;
								min  = mindisp;
							}
							pro.mode = ChannelDataDisplayProperty.MODE_FIX;
						}
					}
					if(AutoTrack){
						pro.mode = ChannelDataDisplayProperty.MODE_AUTO;
					}
				}
				else{
					pro = new ChannelDataDisplayProperty(ch);
				}
				pro.setMaxMin(max, min);
				
				// Y軸を描画
				if(GraphType == GRAPHIC_TYPE_NORMAL){ 
					drawYaxis(gd, 0,0+pro.ChannelArea.y,xbound,pro.ChannelArea.y+pro.ChannelArea.height,yspace,min,max,ch);
				}
				else if(GraphType == GRAPHIC_TYPE_NORMAL_OVERLAP){
					if(CurrentMouseChannelIndex == dispIndex){
						drawYaxis(gd, 0,0+pro.ChannelArea.y,xbound,pro.ChannelArea.y+pro.ChannelArea.height,yspace,min,max,ch);
					}
				}
	
				// 平均値を取得
				// カーソル時間のデータを取得
				int CursorSec=0;
				int CursorIndex=-1;
				long sum = 0;
				int  total = 0;
				double mean = 0.0;
				int cursorValue = 0;
				wintime tm3 = new wintime(GVDispTimeStart);
				long numsec3 = (GVDispTimeRange_ms+999L)/1000L;
				if(GVDispTimeStart.msec()>0) numsec3++;
				
				
				int samplerate = 0;
				wintime tmsmple = new wintime(tm3.year(),tm3.month(),tm3.day(),tm3.hour(),tm3.min(),tm3.sec(),0);
				for(int i=0;i<numsec3 && samplerate==0;i++){
					ChannelOutlineDataCollection  dc1sec = DataBase.getChannelDataCollection1(ch) ;
					if(dc1sec!=null){
						ChannelOutlineData cd1  =  dc1sec.getChannelData(tmsmple);
						if(cd1!=null){
							samplerate = cd1.getSamplerate(); 
						}
					}
					tmsmple.inc_sec();
				}
				// マウスカーソルの位置を表す
				double tempcursortime = CurrentMouseTime.getValueS();
				tempcursortime += (double)CurrentMouseTime.msec()/1000.0;
				tempcursortime += (double)CurrentMouseTime.usec()/1000000.0;
				
				if(numsec3<=60*60){	//　１時間以内の場合
//					int chmax = Integer.MIN_VALUE;
//					int chmin = Integer.MAX_VALUE;
					for(int i=0;i<numsec3;i++){
						int ssec = tm3.getValueS();
						ChannelData cd  = cdlist.get(ssec);
						if(cd!=null){
							//samplerate = cdlist.get(tm3.getValueS()).getSamplerate(); // この代表値と決め付ける
							double temptime = tm3.getValueS();
							int[] d = cd.getData();
							if(d==null) break;
							if(numsec3<=60*60){
								double temptick = 1.0/d.length;
								for(int j=0;j<d.length;j++){
									sum += d[j];
									total++;
									if((tempcursortime >= temptime-temptick/2 )&&(tempcursortime < temptime+temptick/2)){
										cursorValue = d[j];
										CursorIndex = j;
										CursorSec=ssec;
									}
									temptime += temptick;
//									if(chmax<d[j]) chmax = d[j];
//									if(chmin>d[j]) chmin = d[j];
								}
							}
							else{
								double temptick = 1.0;
								total++;
								sum += d[0];
								if((tempcursortime >= temptime-temptick/2 )&&(tempcursortime < temptime+temptick/2)){
									cursorValue = d[0];
									CursorIndex = 0;
									CursorSec=ssec;
								}
								temptime += temptick;
							}
						}
						tm3.inc_sec();
					}
					if(total>0) mean = (double)sum/(long)total;
//					if(chmax!=Integer.MIN_VALUE && chmin != Integer.MAX_VALUE){
//						pro.setMaxMin(chmax, chmin);
//						yaxismax[index] = chmax;
//						yaxismin[index] = chmin;
//
//					}
				}
				else{	//　１時間超の場合
					for(int i=0;i<numsec3/60;i++){
						tm3 = new wintime(tm3.year(),tm3.month(),tm3.day(),tm3.hour(),tm3.min(),0,0);
						ChannelOutlineDataCollection  dc60 = DataBase.getChannelDataCollection60(ch) ;
						if(dc60!=null){
							ChannelOutlineData cd  =  dc60.getChannelData(tm3);
							if(cd!=null){
								//samplerate = cd.getSamplerate(); // この代表値と決め付ける
								total++;
								sum += cd.getMean();
								double temptime = tm3.getValueS();
								if((tempcursortime >= temptime-30 )&&(tempcursortime < temptime+30)){
									cursorValue = (int)cd.getMean();
									CursorIndex = 0;
									CursorSec=tm3.getValueS();;
								}
							}
						}
						tm3.inc_min();
					}
					if(total>0) mean = (double)sum/(long)total;
					
				}
				//-----------------------------------------------------------------------------------------
				//-----------------------------------------------------------------------------------------
				// データを描画
				double delta=0.0;
				boolean continuum = false;
				int     lastData = 0;

				wintime tm2 = new wintime(GVDispTimeStart);
				// 何秒分のデータを書くのか
				long numsec = (GVDispTimeRange_ms+999)/1000;
				if(tm2.msec()>0) numsec++;
				if(GVDispTimeRange_ms<1000) 	numsec = 2;	// 1秒以下の場合は特別に
				
				int drawmode = calcDrawMode(xmax, xbound, samplerate,numsec);
				DrawMode=drawmode;
				//DrawPoint =0;
				// 色を決めて
				pro.setColorCalcMode(ColorCalcMode); // 色の計算モード
				Color dataCol = pro.getDataColor();
				if(ColorMode == COLOR_BW_BLACK_BACK || ColorMode == COLOR_BW_WHITE_BACK) dataCol = ColorDataBW; 

				DrawData2Min=Integer.MAX_VALUE;
				DrawData2Max=Integer.MIN_VALUE;

				
				// 分単位でデータを描画
				if(drawmode>=2){
					// --------------------------------------------------
					drawData3EnterEnd(true,gd,xbound,0+pro.ChannelArea.y,(int)xmax, pro.ChannelArea.y+pro.ChannelArea.height,4,dataCol);
					tm2 = new wintime(tm2.year(),tm2.month(),tm2.day(),tm2.hour(),tm2.min(),0,0);
					for(int i=0;i<numsec/60;i++){
						ChannelOutlineDataCollection  dc60 = DataBase.getChannelDataCollection60(ch) ;
						if(dc60!=null){
							ChannelOutlineData cd  =  dc60.getChannelData(tm2);
							if(cd!=null){
								drawData3( gd, xbound,0+pro.ChannelArea.y,(int)xmax, pro.ChannelArea.y+pro.ChannelArea.height,4,
											min,max,null,tm2,dataCol,cd.getMax(),cd.getMin(),continuum); 
								DrawPoint++;
								continuum = true;
							}
						}
						else{
							continuum = false;
						}
						tm2.inc_min();
					}
					drawData3EnterEnd(false,gd,xbound,0+pro.ChannelArea.y,(int)xmax, pro.ChannelArea.y+pro.ChannelArea.height,4,dataCol);

				}
				// 秒単位でデータを描画
				else if(drawmode==1){
				// --------------------------------------------------
					drawData3EnterEnd(true,gd,xbound,0+pro.ChannelArea.y,(int)xmax, pro.ChannelArea.y+pro.ChannelArea.height,4,dataCol);
					tm2 = new wintime(tm2.year(),tm2.month(),tm2.day(),tm2.hour(),tm2.min(),tm2.sec(),0);
					for(int i=0;i<numsec;i++){
						ChannelOutlineDataCollection  dc1 = DataBase.getChannelDataCollection1(ch) ;
						if(dc1!=null){
							ChannelOutlineData cd  =  dc1.getChannelData(tm2);
							if(cd!=null){
								drawData3( gd, xbound,0+pro.ChannelArea.y,(int)xmax, pro.ChannelArea.y+pro.ChannelArea.height,4,
											min,max,null,tm2,dataCol,cd.getMax(),cd.getMin(),continuum); 
								DrawPoint++;
								continuum = true;
							}
						}
						else{
							continuum = false;
						}
						tm2.inc_sec();
					}
					drawData3EnterEnd(false,gd,xbound,0+pro.ChannelArea.y,(int)xmax, pro.ChannelArea.y+pro.ChannelArea.height,4,dataCol);
				}
				else { // drawmode == 0
					// --------------------------------------------------
//					DrawData2Min=Integer.MAX_VALUE;
//					DrawData2Max=Integer.MIN_VALUE;
					
					for(int i=0;i<numsec;i++){
						int ssec = tm2.getValueS();
						ChannelData cd  = cdlist.get(ssec);
						if(cd!=null){
							int[] d = cd.getData();
							if(d.length<=0){
								tm2.inc_sec();
								continuum = false;
								continue;
							}
							// 標準偏差を計算
							if(i<=60*60){ // 最大1時間まで
								for(int j=0;j<d.length;j++){
									delta +=(d[j]-mean)*(d[j]-mean);
								}
							}
							else{
								delta = 0.0;
							}
							int idx=-1;
							if(ssec == CursorSec){
								idx=CursorIndex;
							}
							// 丁寧に書く
							drawData2( gd, xbound,0+pro.ChannelArea.y,(int)xmax, pro.ChannelArea.y+pro.ChannelArea.height,4,
									min,max,d,tm2,continuum,lastData,dataCol,idx); 
							lastData = d[d.length-1];
							DrawPoint+=d.length;
							continuum = true;
						}
						else{
							continuum = false;
						}
						tm2.inc_sec();
					}
//					if(DrawData2Min != Integer.MAX_VALUE && DrawData2Max != Integer.MIN_VALUE){
//						pro.setMaxMinDisp(DrawData2Max, DrawData2Min);
//						long span = (long)DrawData2Max - (long)DrawData2Min;
//						if(DrawData2MaxSpan<span) DrawData2MaxSpan = span;
//					}
				}
				if(DrawData2Min != Integer.MAX_VALUE && DrawData2Max != Integer.MIN_VALUE){
					pro.setMaxMinDisp(DrawData2Max, DrawData2Min);
					long span = (long)DrawData2Max - (long)DrawData2Min;
					if(DrawData2MaxSpan<span) DrawData2MaxSpan = span;
				}
				//-----------------------------------------------------------------------------------------
				//-----------------------------------------------------------------------------------------
				double sd = 0.0;
				if(total > 1) sd = Math.sqrt((double)delta / (total-1));
				// ｃｈ情報を描画
				if(ShowChannelInfo){
					if(GraphType == GRAPHIC_TYPE_NORMAL){ 
						drawChannelData(gd, 0,0+pro.ChannelArea.y+getFontHeight(ChFontDisplay),xbound,pro.ChannelArea.y+pro.ChannelArea.height,4,ch,mean,sd,cursorValue,samplerate);
					}
				}
				Datamean[index] = mean;
				Datavariant[index] = sd;
				DataChannel[index] = ch;
				DataSampleRate[index]=samplerate;
				// CHをDraggingしてる
				
				if(AreaYaxisDraggingMode==1){
					if(dispIndex==YAxisDraggOrigGraphIndex){
						// Dragg元のｃｈを強調
						gd.drawRect(0, pro.ChannelArea.y, (int)xmax-1, pro.ChannelArea.height);
						AreaYaxisSelected=1;
					}
					else{
						
						int idxxx = getYaxisIndex(CurrentMousePositon.y);
						if(idxxx == dispIndex){
							if(AreaYaxis.contains(CurrentMousePositon)){
								// ドロップ先のＣＨを強調
								gd.drawRect(0, pro.ChannelArea.y, xbound-1, pro.ChannelArea.height);
							}
						}
					}
				}
				
				dispIndex++;
			}
		}
		gd.setClip(null);
		PaintTimeEnd = System.currentTimeMillis();
		PaintTime[PaintTimeIndex++] = PaintTimeCurrent = PaintTimeEnd - PaintTimeStart;
		if(PaintTimeIndex>=100) PaintTimeIndex=0;
		//calcCurrentMouseTime(CurrentMousePositon.x-xbound,(int)xmax-xbound);
		if(!IsPrint){
			// カーソルを書く
			if(CursorDisplay != 0){
				drawCursor(gd,xbound,0,(int)xmax,ybound);
			}
			// 左下の情報を描画する
			//drawMisc(gd,AreaMisc.x,AreaMisc.y ,AreaMisc.x+AreaMisc.width,AreaMisc.y+AreaMisc.height);
			//drawMisc();
			drawDraggMarker(gd,xbound,0,(int)xmax,ybound);
			
			if(XAxisProp.DrawClock) drawClock(gd,AreaMisc);
		}
		
		drawMarker(gd,xbound,0,(int)xmax,ybound); // マーカを描画する
		if(ChannelNumChangeFlag){
			onChangeDisplayChannel();
			ChannelNumChangeFlag=false;
		}
	
		
		
	}
	// TODO: アナログ時計を描画、もっと恰好よく書けないかなあ
	private static final int Clock_View_Size  = 35;
	private static final int Clock_View_margin = 5;
	private static final int Clock_View_radius = (Clock_View_Size-2*Clock_View_margin)/2;
	private static final int Clock_View_hour = Clock_View_radius - 6;
	private static final int Clock_View_min = Clock_View_radius - 3;
	private static final int Clock_View_sec1 = Clock_View_radius-1;
	private static final int Clock_View_sec2 = Clock_View_radius;

	private void drawClock(Graphics2D g,Rectangle r){
		if(r.width < Clock_View_Size || r.height < Clock_View_Size){
			return;
		}
		Graphics2D g2 = (Graphics2D)g;
		g2.setColor(ColorXAxis);
		// 中心をセンターにする
		Stroke strokeorg = g2.getStroke();
		int tx = r.x + r.width - Clock_View_Size/2;
		int ty = r.y + r.height/2;
		g2.translate(tx , ty);
		g2.drawOval(-Clock_View_radius, -Clock_View_radius, 2*Clock_View_radius, 2*Clock_View_radius);
		int hour = GVDispTimeStart.hour();
		int min = GVDispTimeStart.min();
		int sec = GVDispTimeStart.sec();
		double h = 2*Math.PI*(hour % 12)/12;
		double m = 2*Math.PI*min/60;
		double s = 2*Math.PI*sec/60;
		//g2.setStroke(BoldStroke2);
		g2.drawLine(0, 0, (int)(Clock_View_hour*Math.sin(h)),-(int)(Clock_View_hour*Math.cos(h)));
		g2.drawLine(0, 0, (int)(Clock_View_min*Math.sin(m)),-(int)(Clock_View_min*Math.cos(m)));
		g2.setStroke(strokeorg);
		g2.drawLine((int)((Clock_View_sec1)*Math.sin(s)), -(int)((Clock_View_sec1)*Math.cos(s)), (int)(Clock_View_sec2*Math.sin(s)),-(int)(Clock_View_sec2*Math.cos(s)));
		g2.setStroke(strokeorg);
		g2.translate(-tx , -ty);
		
	}
	// ----------------------------------
	// 印刷
	@Override
	public int print(Graphics g, PageFormat pf, int arg2)
			throws PrinterException {
		if(arg2 >= 1) return Printable.NO_SUCH_PAGE;
		
		Graphics2D g2 = (Graphics2D)g;
		g2.translate(pf.getImageableX(), pf.getImageableY());
		g2.scale(0.5,0.5);
		double hight = pf.getImageableHeight();
		double width = pf.getImageableWidth();
		Dimension dm = new Dimension();
		dm.setSize(width*2,hight*2);
		
		CurrentDPI=72; 
		IsPrint=true;
		
		int cmorg = ColorMode;
		if(winchkgProperty.PrintColorAlwaysWhite){
			switch(ColorMode){
			case COLOR_BW_WHITE_BACK:
			case COLOR_BW_BLACK_BACK:
				setColorMode(COLOR_BW_WHITE_BACK);
				break;
			case COLOR_COLOR_BLACK_BACK:
			case COLOR_COLOR_WHITE_BACK:
				setColorMode(COLOR_COLOR_WHITE_BACK);
				break;
			}
		}
		if(GraphType== GRAPHIC_TYPE_NORMAL || GraphType== GRAPHIC_TYPE_NORMAL_OVERLAP ){
			paintComponetSubNomal(g2,dm);
		}
		else{
			paintComponetSubDrum(g2,dm);
		}
		CurrentDPI=96;
		IsPrint=false;
		setColorMode(cmorg);
		return 0;
	}
	
//	private void putData(int ch,ChannelDataCollection dcol){
//		DataBase.putData(ch,dcol);
//	}
	//------------------------------------------------
	// ＣＨデータを削除
//	public void removeCh(int chname){
//		//getChannel().remove(chname);
//		removeChannel(chname);
//		ChannelNumChangeFlag=true;
//	}
	
//	private void removeChannel(int ch){
//		DataBase.removeChannel(ch);
//	}
//	private void removeData(int ch){
//		DataBase.removeData(ch);
//	}
	// ------------------------------
	// すべてを書き直し
	public void repaintAll(){
		Rectangle r = new Rectangle(0,0,getWidth(),getHeight());
		repaint(r);
	}
	// ------------------------------------------
	// データを初期化する
	public void resetContent(){
		GVDispTimeStart = new wintime();
		//LatestTime = DispTimeStart;
		//OldestTime = DispTimeStart;
		//getChannel().clear();
		clearChannel();
		//Data.clear();
		//getDataTreeMap().clear();
		clearData();
		setDispTimeRange_ms(100);
		ChannelNumChangeFlag=true;
	}
	
	
	/**
	 * ｃｈ表示プロパティを制御する
	 * @param mode
	 *  0: 表示してるＣＨのみ
	 *  1: 全ＣＨ　（channel.ini） 　が対象
	 */
	public void selectDisplayChannel(int mode){
		if(mode != 0 && mode != 1) return;
		if(DlgChannelDisplayProperty != null){
			if(DlgChannelDisplayProperty.isShowing()){
				DlgChannelDisplayProperty.dispose();
			}

		}
		DlgChannelDisplayProperty = new DialogChannelDisplayProperty(getParentFrame());
		DlgChannelDisplayProperty.setParent(this);// ActionPerformedが呼びさされる
		
		Point pt = new Point(100,100);
		try{
			pt = mYSelf.getLocationOnScreen();
		}
		catch(IllegalComponentStateException e){
		}
		DlgChannelDisplayProperty.setLocation(pt);
		
		
		int size=0;
		Set<Integer> chs = null;
		if(mode == 0) {
			size = sizeChannel();
			DlgChannelDisplayProperty.setMode(0);
		}
		else if(mode==1){
			//ChConfig.save(INIT_FILE);
			chs = ChConfig.getChannelKeySet();
			size = chs.size();
			DlgChannelDisplayProperty.setMode(1);
		}
		
		intitChannelConfig();
		
		int [] chary = new int[size];
		boolean[] dispary  = new boolean[size];
		boolean[] puary = new boolean[size];
		String[] station = new String[size];
		String[] component = new String[size];
		int[] tblindex = new int[size];
		double[] factor = new double[size];
		String[] unit = new String[size];
		long[] fullscale = new long[size];
		boolean[] mask = new boolean[size];
		double[] lat = new double[size];
		double[] lon = new double[size];
		double[] alt = new double[size];
		
		int index = 0;
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = null;
			if(mode == 0) {
				ite = iteratorChannel();
			}
			else if(mode==1){
				ite = chs.iterator();
			}
			while(ite.hasNext()){
				boolean disp,pu;
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro != null){
					if(pro.hide == 0) disp=true;
					else			disp=false;
					if(pro.unitType == ChannelDataDisplayProperty.UNIT_TYPE_CONV)  pu=true;
					else pu = false;
					chary[index]=ch;
					dispary[index]=disp;
					puary[index]=pu;
					station[index] = pro.getStation();
					component[index] = pro.getComponent();
					tblindex[index] = pro.getChannelTableIndex();
					factor[index] = pro.unitFactor;
					unit[index] = pro.unitString;
					fullscale[index] = pro.FullScale;
					mask[index] = pro.Mask;
					lat[index] = pro.Latitude;
					lon[index] = pro.Longitude;
					alt[index] = pro.Altitude;
									
					index++;
				}
			}
		}
		DlgChannelDisplayProperty.setData(chary,dispary,puary,station,component,tblindex,factor,unit,fullscale,mask,
				lat,lon,alt);
		DlgChannelDisplayProperty.setVisible(true);
		
	}
	
	// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.

	public void setAmpLabelEnable(boolean en){
		DrawYLabel=en;
	}
	public boolean getAmpLabelEnable(){
		return DrawYLabel;
	}

	// --------------------------------
	// すべての振幅を増やす/減らす
	public void setAmplitudeOffset(boolean set){
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro != null){
					if(pro.hide == 0 && !pro.Mask){
						if(set){
							pro.offset = pro.mean;
							pro.offsetEnable=true;
						}
						else{
							pro.offsetEnable=false;
						}
						ChConfig.setData(ch, pro);
					}
				}
			}
		}
		repaintAll();
	}
	
	public void setAutoTrack(boolean autoTrack) {
		AutoTrack = autoTrack;
	}
	
	public void setColorCalcMode(int calcmode){
		if(ColorCalcMode != calcmode){
			ColorCalcMode = calcmode;
			repaintAll();
		}
	}
	
	// --------------------------------
	// カラーモード
	public void setColorMode(int col){
		if(col==COLOR_BW_WHITE_BACK){
			ColorMode=col;
			ColorBackGround = Color.white;
			ColorTimeSample = Color.orange;
			ColorTime1Sec = Color.blue;
			ColorTime1min = Color.red;
			ColorTime1hour = Color.black;
			ColorXAxis = Color.black;
			ColorYAxis = Color.black;
			ColorXDragging = new Color(100,100,100,50);
			ColorTextInfo = Color.black;
			ColorCursor = Color.gray;
			ColorDataBW = Color.gray;
			ColorMarker0 = Color.gray;
			ColorMarker1 = Color.gray;
		}
		else if(col==COLOR_BW_BLACK_BACK){
			ColorMode=col;
			ColorBackGround = Color.black;
			ColorTimeSample = Color.gray;
			ColorTime1Sec = Color.gray;
			ColorTime1min = Color.gray;
			ColorTime1hour = Color.gray;
			ColorXAxis = Color.white;
			ColorYAxis = Color.white;
			ColorXDragging = new Color(100,100,100,50);
			ColorTextInfo = Color.white;
			ColorCursor = Color.gray;
			ColorDataBW = Color.white;
			ColorMarker0 = Color.gray;
			ColorMarker1 = Color.gray;
		}
		else if(col==COLOR_COLOR_BLACK_BACK){
			ColorMode=col;
			//ColorSaturate=0.8f;
			ColorBrightness = 1;
			ColorBackGround = Color.BLACK;
			ColorTimeSample = Color.orange;
			ColorTime1Sec = Color.blue;
			ColorTime1min = Color.red;
			ColorTime1hour = Color.black;
			ColorXAxis = Color.white;
			ColorYAxis = Color.white;
			ColorXDragging = new Color(200,200,200,50);
			ColorTextInfo = Color.white;
			ColorCursor = Color.orange;
			ColorDataBW = Color.gray;
			ColorMarker0 = Color.red;
			ColorMarker1 = Color.blue;
		}
		else if(col==COLOR_COLOR_WHITE_BACK){
			ColorMode=col;
		//	ColorSaturate=0.8f;
			ColorBrightness = 2;
			ColorBackGround = Color.white;
			ColorTimeSample = Color.orange;
			ColorTime1Sec = Color.blue;
			ColorTime1min = Color.red;
			ColorTime1hour = Color.black;
			ColorXAxis = Color.black;
			ColorYAxis = Color.black;
			ColorXDragging = new Color(100,100,100,50);
			ColorTextInfo = Color.black;
			ColorCursor = Color.orange;
			ColorDataBW = Color.gray;
			ColorMarker0 = Color.red;
			ColorMarker1 = Color.blue;
		}
		
		
	}
	public void setConvSigmaSNR(boolean convSigmaSNR) {
		if(ConvSigmaSNR != convSigmaSNR){
			ConvSigmaSNR = convSigmaSNR;
			repaintAll();
		}
	}

	public void setDispTimeRange_ms(long time) {
		GVDispTimeRange_ms = time;
		if(ParentPanel!=null) ParentPanel.onChangeTimeRange((int)GVDispTimeRange_ms);
	}
	// --------------------------------------------------
	// エクスポートするフォルダの指定
	public void setExportFolder(String dir){
		ExportFolder=dir;
	}
	// ------------------------------------------
	// 指定IDに物理換算値をセットする
	private void setPU(int id){
		ChannelDataDisplayProperty pro = getChannelInfo(id);
		if(pro == null) return;
		switch(ChannelPropDialog.ConvMode){
		case DialogSingleChannelAmplitudeProperty.UnitModePU:
			pro.unitType = ChannelDataDisplayProperty.UNIT_TYPE_CONV;
			break;
		case DialogSingleChannelAmplitudeProperty.UnitModeDigit:
		default:
			pro.unitType = ChannelDataDisplayProperty.UNIT_TYPE_DIGIT;
		}
		pro.unitString = ChannelPropDialog.UnitText;
		pro.unitFactor = ChannelPropDialog.Factor;
		pro.FullScale = ChannelPropDialog.FullScale;
		ManualYScale.put(id, pro);
		ChConfig.setData(id, pro);
		getwinDataBase().setFullScale(id, (int)pro.FullScale );
	}
	public void setShowChannel(int num){
		int ch = getDisplayChannel();
		if(num < ch){
			for(int i=0;i<ch-num;i++){
				doChannelDec();
			}
		}
		else{
			for(int i=0;i<num-ch;i++){
				doChannelInc();
			}
		}
	}
	//-------------------------
	public void setShowChannelInfo(boolean s) {
		ShowChannelInfo = s;
	}
	public void setShowTimeSamplePoint(boolean set){
		ShowTimeSamplePoint = set;
	}
	public boolean getShowTimeSamplePoint(){
		return ShowTimeSamplePoint;
	}
	
	public void setwinDataBase(winDataBase d){
		DataBase = d;
		initFullScale();
	}
	// ------------------------------
	// すべてのＣＨを表示する
	public void showAllChannel() {
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro != null){
					pro.hide = 0;
					ManualYScale.put(ch,pro);
					ChConfig.setData(ch, pro);
	
				}
			}
		}
		repaintAll();
	}
	// -------------------------------------------------------------------
	// ------------------------------------------
	// n個下のCHANNELを表示する
	private void showChannelDown(int count){
		while(count>0){
			int disp1 = getFirstDisplayChannel();
			int disp2 = getLastDisplayChannel();
			int disp3 = -1;
			if(disp2 != -1) disp3=getNextChannel(disp2);
			if(disp1 !=-1 && disp3 != -1){
				 ChannelDataDisplayProperty pro = getChannelInfo(disp1);
				 pro.hide = 1;
				 ManualYScale.put(disp1, pro);
				 ChConfig.setData(disp1, pro);
				 pro = getChannelInfo(disp3);
				 pro.hide = 0;
				 ManualYScale.put(disp3, pro);
				 ChConfig.setData(disp3, pro);
			}
			count--;
		}
	}
	// ------------------------------------------
	// n個上のCHANNELを表示する
	private void showChannelUp(int count){
		while(count>0){
			int disp1 = getFirstDisplayChannel();
			int disp2 = getLastDisplayChannel();
			int disp3 = -1;
			if(disp1 != -1) disp3=getPrevChannel(disp1);
			if(disp2 !=-1 && disp3 != -1){
				 ChannelDataDisplayProperty pro = getChannelInfo(disp2);
				 pro.hide = 1;
				 ManualYScale.put(disp2, pro);
				 ChConfig.setData(disp2, pro);
				 pro = getChannelInfo(disp3);
				 pro.hide = 0;
				 ManualYScale.put(disp3, pro);
				 ChConfig.setData(disp3, pro);
			}
			count--;
		}
	}
	private int sizeChannel(){
		if(DataBase==null) return 0;
		return DataBase.sizeChannel();
	}
	// ------------------------------------------
	// グラフエリアのx座標オフセットから時刻を計算する
	private wintime x2time(int xoffset,int xmax,wintime starttime,long timespanms){
		double tmoffset = ((double)xoffset)/(xmax-4); // -4 space)
		double tmofsdbl = tmoffset * (double)(timespanms);
		int tmoffsetms = (int)(tmofsdbl);
		int tmoffsetus = (int)((tmofsdbl - (double)tmoffsetms)*1000.0);
		wintime ret = new wintime(starttime);
		ret.inc_msec(tmoffsetms);
		ret.setUSec(tmoffsetus);
		return ret;
//		int tmoffsetint = (int)(tmoffset * (double)(timespanms)+0.5);
//		wintime ret = new wintime(starttime);
//		ret.inc_msec(tmoffsetint);
//		return ret;
	}
	// ------------------------------------------
	// Y軸の表示をズームアップダウン
	private void YAxisScaleSome(ChannelDataDisplayProperty pro,int up){
		long span;
		long cent;
		span = pro.getAmplitudeValue();
		cent = pro.getCenterValue();
		
		if(up != 0){
			int nspan = gerAvailableSpan(cent,span);
			int max = intAdd(cent,(long)nspan);
			int min = intSub(cent,(long)nspan);
			//if(max!=Integer.MAX_VALUE && min != Integer.MIN_VALUE){
				pro.setMaxMin(max, min);
			//}
		}
		else{
			span /= 4;
			if(span>=2){
				pro.setMaxMin(cent + span, cent - span);
			}
		}
		
	}
	// ------------------------------------------
	// Y軸の表示chをシフト
	private void YAxisShiftSome(ChannelDataDisplayProperty pro,int up){
		long max;
		long min;
		long span;
		max = pro.getMax();
		min = pro.getMin();
		span = (max-min)/5;
		
		if(up != 0){
			if(max + span<Integer.MAX_VALUE) {
				pro.setMaxMin(pro.getMax()+span, pro.getMin()+span);
			}
			else pro.setMax(Integer.MAX_VALUE);
		
		}
		else{
			if((min - span)>Integer.MIN_VALUE){
				pro.setMaxMin(pro.getMax()-span, pro.getMin()-span);
			}
			else pro.setMin(Integer.MIN_VALUE);
		}
		
	}
	@Override
	public void keyPressed(KeyEvent ke) {
		int kc = ke.getKeyCode();
		int mod = ke.getModifiersEx();
		switch(kc){
		case KeyEvent.VK_LEFT:
			if(0 != (mod & InputEvent.SHIFT_DOWN_MASK)){
				doPageDecTime();
			}
			else if(0 != (mod & InputEvent.CTRL_DOWN_MASK)){
				doZoomOut();
			}
			else{
				doDecTime();
			}
			break;
		case KeyEvent.VK_RIGHT:
			if(0 != (mod & InputEvent.SHIFT_DOWN_MASK)){
				doPageIncTime();
			}
			else if(0 != (mod & InputEvent.CTRL_DOWN_MASK)){
				doZoomIn();
			}
			else{
				doIncTime();
			}
			break;
		case KeyEvent.VK_UP:
			if(0 != (mod & InputEvent.SHIFT_DOWN_MASK)){
			}
			else if(0 != (mod & InputEvent.CTRL_DOWN_MASK)){
				
			}
			else{
				incDecAmplitude(false);
			}
			
			break;
		case KeyEvent.VK_DOWN:
			if(0 != (mod & InputEvent.SHIFT_DOWN_MASK)){
			}
			else if(0 != (mod & InputEvent.CTRL_DOWN_MASK)){
			}
			else{
				incDecAmplitude(true);
			}
			
			break;
		case KeyEvent.VK_PAGE_UP:
			if(0 != (mod & InputEvent.SHIFT_DOWN_MASK)){
				doChannelPageUp();
			}
			else if(0 != (mod & InputEvent.CTRL_DOWN_MASK)){
			}
			else if(0 != (mod & InputEvent.ALT_DOWN_MASK)){
				
			}
			else{
				doChannelUp();
			}
			break;
		case KeyEvent.VK_PAGE_DOWN:
			if(0 != (mod & InputEvent.SHIFT_DOWN_MASK)){
				doChannelPageDown();
			}
			else if(0 != (mod & InputEvent.CTRL_DOWN_MASK)){
			}
			else if(0 != (mod & InputEvent.ALT_DOWN_MASK)){
				
			}
			else{
				doChannelDown();
			}
			break;
		//case KeyEvent.VK_INSERT:
			//	doChannelInc();
			//break;
		//case KeyEvent.VK_DELETE:
		//	doChannelDec();
		//	break;
			case KeyEvent.VK_DELETE:
				removeChannel(-1);
			break;
		case KeyEvent.VK_0:
			//doMakerAdd(0);
			break;
		case KeyEvent.VK_1:
			//doMakerAdd(1);
			break;
		}
	}
	/**
	 * ＣＨを削除する
	 * @param i
	 */
	private void removeChannel(int ch) {
		// TODO Auto-generated method stub
		if(ch==-1){
			if(AreaYaxisSelected==1){
				DataBase.removeData(CurrentMouseChannel);
				DataBase.removeChannel(CurrentMouseChannel);
			}
		}
		else{
			DataBase.removeData(ch);
			DataBase.removeChannel(ch);
		}
		repaintAll();
	}
	@Override
	public void keyReleased(KeyEvent arg0) {
	}
	@Override
	public void keyTyped(KeyEvent ke) {
		switch(ke.getKeyChar()){
		case 'S':
		case 's':
			doSingleScale();
			break;
		case 'A':
		case 'a':
			doAutoScale();
			break;
		case '[':
			decGraphX(10);
			break;
		case ']':
			incGraphX(10);
			break;
		case 'D':
		case 'd':
			int mod = ke.getModifiersEx();
			if(0 == (mod & InputEvent.ALT_DOWN_MASK)){
				viewDataTable();
			}
			break;
		case 'h':
			ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentMousePositon);
			if(pro != null){
				pro.hide=1;
				ManualYScale.put(pro.getChannel(),pro);
				ChConfig.setData(pro.getChannel(), pro);
			}				
			repaintAll();
			break;
		case 'H':
			showAllChannel();
			break;
		case '+':
			doChannelInc();
			break;
		case '-':
			doChannelDec();
			break;
		
		}
	}
	//------------------------------------------
	private boolean HasKeyFocus = false;
	private boolean hasKeyFocus(){
		return HasKeyFocus;
	}

	@Override
	public void focusGained(FocusEvent arg0) {
		HasKeyFocus=true;
		repaintAll();
		//System.out.println("FocusGain");
	}

	@Override
	public void focusLost(FocusEvent arg0) {
		HasKeyFocus=false;
		repaintAll();
		//System.out.println("FocusLose");
	}
	/**
	 * 表示されているチャネルのリストを返す
	 * @return
	 */
	private ArrayList<Integer> getDisplayChannelList(){
		ArrayList<Integer> ret = new ArrayList<Integer>();
		synchronized(DataBaseLock()){
			Iterator<Integer> ite = iteratorChannel();
			while(ite.hasNext()){
				int ch = ite.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(pro == null) continue;
				if(pro.hide == 0 && !pro.Mask) {
					ret.add(ch);
				}
			}
		}
		return ret;
	}
	/**
	 * 表示開始時刻から２秒分をデータテーブルで見る
	 */
	public void viewDataTable(){
		// CurrentMouseTime CursorDisplay
		if(mDataTableViewDialog==null){
			mDataTableViewDialog=new DataTableViewDialog(getParentFrame2());
			//ParentPanel.add(mDataTableViewDialog, BorderLayout.EAST);
			//mDataTableViewDialog.setFloatable(true);
			mDataTableViewDialog.setVisible(false);
		}
		if(mDataTableViewDialog.isVisible()){
			mDataTableViewDialog.setVisible(false);
		}
		else{
			mDataTableViewDialog.setWinDataBase(DataBase);
			if(CursorDisplay!=0){
				wintime wt = new wintime(CurrentMouseTime);
				wt.dec_sec();
				mDataTableViewDialog.setDispStartTime(wt);
				mDataTableViewDialog.setFirstVisibleTime(CurrentMouseTime);
			}else{
				mDataTableViewDialog.setDispStartTime(GVDispTimeStart);
				mDataTableViewDialog.setFirstVisibleTime(GVDispTimeStart);
			}
	
			
			wintime[] cwm = getDialogTimeMarker().getWinTime();
			if(cwm!=null){
				if(cwm.length>=1){
					int[] idxmk = getDialogTimeMarker().getMarkerIndex();
					for(int i=0;i<cwm.length;i++){
						mDataTableViewDialog.setMarkerTime(idxmk[i],cwm[i]);
					}
				}
			}
			
			mDataTableViewDialog.setDispTimeSpan(2);
			mDataTableViewDialog.setDisplayChannel(getDisplayChannelList());
			if(mDataTableViewDialog.update()){
				mDataTableViewDialog.setVisible(true);
			}
		}
	}
	//-----------------------------------------------------------------------------------------------------
	// ドラム記録形式のビュー
	//-----------------------------------------------------------------------------------------------------
	private int DrumColNumberMax = 2;	// 横に最大いくつ並べるか
	private int DrumColNumber = 3;		// 実際に横にいくつ並んでいるか
	private int DrumRowNumber = 1;		// 実施に何行あるか
	private ArrayList<Rectangle> DrumRect = new ArrayList<Rectangle>();
	private ArrayList<Integer> DrumRectCh = new ArrayList<Integer>();
	
	/**
	 * ドラムモード描画 メイン
	 * @param g
	 * @param dm
	 */
	private void paintComponetSubDrum(Graphics g,Dimension dm){
		// 描画パフォーマンス測定
		long painttime = System.currentTimeMillis();
//		if((painttime - PaintTimeStart < 50)&&!CursorOut) {
//			return;
//		}
		PaintTimeStart = painttime;
		super.paintComponents(g);	
		Graphics2D gd = (Graphics2D)g;
		gd.setBackground(ColorBackGround);
		gd.clearRect(0, 0, dm.width, dm.height);
		
		int xmax = dm.width;
		int ymax = dm.height;
		//int xbound = 250;	// 左から250まで
		int ybound = (int)(ymax-XaxisAreaHeight-XguideAreaHeight);	// 
		//AreaMisc = new Rectangle(0,ybound,xbound,(int)ymax-ybound);
		
		int chnum = getChannelNumber();
		ChannelMax = chnum;
		if(chnum <= 0){
			Graphics2D gd2 = (Graphics2D)g;
			gd2.setColor(Color.gray);
			Font fon = g.getFont();
			gd2.setFont(fon.deriveFont(20f));
			drawTools.drawText(gd2, "Drop win files here", dm.width/2-20, dm.height/2, drawTools.POSITION_CENTER_BOTTOM);
			gd2.setFont(fon.deriveFont(14f));
			drawTools.drawText(gd2, "drum recorder mode" , dm.width/2-20, dm.height/2+30, drawTools.POSITION_CENTER_BOTTOM);
			return;
		}
		int nnn = getDisplayChannel();
		if(nnn<=0){
			doChannelInc();
			nnn = getDisplayChannel();
		}
		if(nnn>CH_DISPLAY_MAX) nnn=CH_DISPLAY_MAX;
		if(GraphNum != nnn){
			ChannelNumChangeFlag=true;
			GraphNum = nnn;
		}
		
		// 配置を決める
		DrumRowNumber=(GraphNum+DrumColNumberMax-1)/DrumColNumberMax;
		DrumRect.clear();
		DrumRectCh.clear();
		int width;
		if(DrumRowNumber==1){// １行なら横方向に分割する
			DrumColNumber = GraphNum;
		}
		else{
			DrumColNumber = DrumColNumberMax;
		}
		drumTimeManagement(); //　表示時刻、スパンの取得
		
		calcMaxMin(); // 最大値、最小値の計算
		
		// 各グラフの領域を作成
		width = xmax / DrumColNumber;
		int height = ymax / DrumRowNumber;
		int row=0;
		for(int i=0;i<GraphNum;i++){
			Rectangle r = new Rectangle((i%DrumColNumber)*width,row*height,width-1,height-1);
			DrumRect.add(r);
			DrumRectCh.add(-1);
			if((i%DrumColNumber) == (DrumColNumber-1)){
				row++;
			}
		}
		
		calcDrumMarker();// マーカー用の座標を計算する
		
		// データを描画する
		synchronized(DataBaseLock()){
			Iterator<Integer> itech = iteratorChannel();
			int dispindex=0;
			for(int index2=0;itech.hasNext();index2++){
				int ch = itech.next();
				ChannelDataDisplayProperty pro = getChannelInfo(ch);
				if(isChannelHide(ch)){
					continue;
				}
				if(DrumRect.size()<=dispindex){
					break;
				}
				//ChannelDataCollection dcol = getChannelDataCollection(ch);
				Rectangle r = DrumRect.get(dispindex);
				g.translate(r.x, r.y);
				// 描画
				paintSingleDrum((Graphics2D)g,new Dimension(r.width,r.height),ch,pro);
				DrumRectCh.set(dispindex, ch);
				g.translate(-r.x, -r.y);
				dispindex++;
			}
		}
		drawMisc();
		// カーソル時刻を描画
		if(!IsPrint && DrawCursorTimeInfo && CursorDisplay!=0){
			Font fonorg = g.getFont();
			Font fon2 = g.getFont().deriveFont(10f);
			g.setFont(fon2);
			g.setColor(ColorCursor);
			int pos = drawTools.POSITION_LEFT_BOTTOM | drawTools.FILL_RECT;
			int y=CurrentMousePositon.y-20;
			int x=CurrentMousePositon.x;
			if(50 > CurrentMousePositon.y){
				y += 40;
			}
			if(dm.width - 100<CurrentMousePositon.x){
				x -= 120;
			}
			drawTools.drawText(g, CurrentMouseTime.fmtDay() + " " + CurrentMouseTime.fmtSec(), x,y , pos);
			g.setFont(fonorg);
		}

		// 描画パフォーマンス測定
		PaintTimeEnd = System.currentTimeMillis();
		PaintTime[PaintTimeIndex++] = PaintTimeCurrent = PaintTimeEnd - PaintTimeStart;
		if(PaintTimeIndex>=100) PaintTimeIndex=0;

		if(ChannelNumChangeFlag){
			onChangeDisplayChannel();
			ChannelNumChangeFlag=false;
		}

	}	
	
	/**
	 * 表示範囲からMAX、MINを計算して channelPropertyにセットする
	 */
	private void calcMaxMin(){
		// 振幅を決めるため 最大値最小値を求める
		int maxtable[] = new int[GraphNum];
		int mintable[] = new int[GraphNum];
		int yaxiscen[] = new int[GraphNum];
		int chtable[] = new int[GraphNum];
		long ampmax = 0L;
		int maxall = Integer.MIN_VALUE;
		int minall = Integer.MAX_VALUE;
		synchronized(DataBaseLock()){
			Iterator<Integer> itech = iteratorChannel();
			int dispindex=0;
			for(int index2=0;itech.hasNext();index2++){
				int ch = itech.next();
				if(isChannelHide(ch)){
					continue;
				}
				Point poi =getYMaxMin(ch, DispTimeStartDrum,DispTimeRange_msDrum);
				maxtable[dispindex] = poi.x;
				mintable[dispindex] = poi.y;
				if(!(poi.x==Integer.MIN_VALUE || poi.y==Integer.MAX_VALUE)){
					if(poi.x > maxall) maxall = poi.x;
					if(poi.y < minall) minall = poi.y;
					yaxiscen[dispindex] = (int)(((long)poi.x + (long)poi.y)/2L);
					chtable[dispindex]=ch;
					if(ampmax < (long)poi.x -(long)poi.y){
						ampmax = (long)poi.x -(long)poi.y;
					}
				}
				dispindex++;
				if(dispindex>GraphNum){
					System.err.println("GraphicView: error calc max min");
					break;
				}
			}
		}
		
		// 振幅を設定する
		for(int i=0;i<chtable.length;i++){
			int ch=chtable[i];
			ChannelDataDisplayProperty pro = getChannelInfo(ch);
			int max=maxtable[i];
			int min=mintable[i];
			if(pro != null){
				if(pro.mode == ChannelDataDisplayProperty.MODE_FIT_SCALE){ // fit scale all channel max
					max = intAdd(yaxiscen[i],(ampmax)/2);
					min = intSub(yaxiscen[i],(ampmax)/2);
					pro.mode = ChannelDataDisplayProperty.MODE_FIX;
				}
				else if(pro.mode==ChannelDataDisplayProperty.MODE_FIX){ //  manual scale (FIX)
					max = (int)pro.getMax();
					min = (int)pro.getMin();
				}
				else if(pro.mode==ChannelDataDisplayProperty.MODE_AUTO){ // auto scale (FIX scale once)
					if(max!=Integer.MIN_VALUE && min != Integer.MAX_VALUE){
						pro.mode = ChannelDataDisplayProperty.MODE_FIX;
					}
				}
				if(AutoTrack){
					pro.mode = ChannelDataDisplayProperty.MODE_AUTO;
				}
			}
			else{
				pro = new ChannelDataDisplayProperty(ch);
			}
			if(max==Integer.MIN_VALUE || min == Integer.MAX_VALUE){
				return;
			}
			pro.setMaxMin(max, min);
			ChConfig.setData(ch, pro);
		}
	}
	
	
	/**
	 * 　時間スパン（1行分）
	 */
	private int DrumTimeSpanSec = 60;			// 時間スパン（1行分）
	private int DrumTimeRowCount = 10;			// 行数
	private int DrumTimeDiv = 10;				// １行あたりの時間の分割数
	private int DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv;		// １分割あたりの時間（秒）
	private int DrumTimeDivSub = 60;			// １行あたりの補助目盛の分割数
	private int DrumLabelStep = 5;				// 縦の時間をいくつおきに時刻表示するか。
	private int DrumGraphXbound = 80;			// 左側の表示エリア
	private int DrumGraphYbound = 30;			// 下側の表示エリア
	private int DrumGraphXMargin = 20;			// 左側の表示エリア
	private int DrumGraphYTopMargin = 20; 		// 上部のマージン
	
	
	/**
	 * １ｃｈ分のデータを描く
	 * @param g
	 * @param dm
	 * @param dcol
	 * @param pro
	 */
	private void paintSingleDrum(Graphics2D g,Dimension dm,int ch,ChannelDataDisplayProperty pro){
		g.setColor(ColorXAxis);
		//g.drawRect(1, 1, dm.width, dm.height);
		
		int x1 = DrumGraphXbound;
		int y1 = dm.height - DrumGraphYbound;
		
		// チャンネル番号を描く
		g.setColor(ColorTextInfo);
		String channelname = new String();
		if(ShowChannelNumber){
			channelname += String.format("[%04X]",pro.getChannel());
		}
		if(ShowStation){
			channelname += (" " + pro.getStation());
		}
		if(ShowComponent){
			channelname += (" " + pro.getComponent());
		}
		if(!channelname.isEmpty()){
			drawTools.drawText(g, channelname, dm.width/2, 2, drawTools.POSITION_LEFT_TOP);
		}
		// 日付を描く
		wintime wt = new wintime(DispTimeStartDrum);
		drawTools.drawText(g, wt.fmtDay(), 10, 2, drawTools.POSITION_LEFT_TOP);
		// 時間スパンを描く
//		if(!IsPrint){
//			int span = DrumTimeSpanSec *DrumTimeRowCount;
//			String mes = new String();
//			if(span>=3600*24   ) mes = String.format("SP=%dday", span/(3600*24));		
//			else if(span>=3600) mes = String.format("SP=%dhr", span/3600);
//			else if(span >= 60) mes = String.format("SP=%dmin", span/60);
//			else				mes = String.format("SP=%dsec", span);
//			drawTools.drawText(g, mes, 10, dm.height-2, drawTools.POSITION_LEFT_BOTTOM);
//		}
		
		// 時間軸を書く
		g.translate(x1,y1);
		drawDrumTimeAxis(g,new Dimension(dm.width-x1,DrumGraphYbound));
		g.translate(-x1,-y1);
		
		//グラフエリアの座標計算
		float hs = (float)(y1 - DrumGraphYTopMargin)/DrumTimeRowCount;// 1グラフあたりの高さ
		if(hs<1f) hs = 1f;
		// 時刻ラベルをいくつ置きに表示するか
		int labelstep = DrumLabelStep;
		while(labelstep*hs < 20f){
			labelstep *= 2;// 高さがなければ間引く
		}
		
		Object obj = getTypicalChannelDataCol(pro.getChannel(),DispTimeStartDrum,DrumTimeSpanSec*DrumTimeRowCount);
		if(obj == null) return; // データがない
		int samplerate=0;
		int center=0;
		if(obj instanceof ChannelData){
			samplerate = ((ChannelData)obj).getSamplerate();
			center     = (int)((ChannelData)obj).getMean();
		}
		else if(obj instanceof ChannelOutlineData){
			samplerate = ((ChannelOutlineData)obj).getSamplerate();
			center     = (int)((ChannelOutlineData)obj).getMean();
		}
		int drawmode = calcDrawMode(dm.width-DrumGraphXbound, 0,samplerate,DrumTimeSpanSec);
		DrawMode=drawmode; // DrawModeはここで決めてしまう

		for(int i=0;i<DrumTimeRowCount;i++,wt.inc_sec(DrumTimeSpanSec)){
			// Y軸とラベルの描画
			g.translate(0, (int)(i*hs + DrumGraphYTopMargin));
			if(i%labelstep == 0 || i==DrumTimeRowCount-1) 	drawDrumYAxis(g,new Dimension(DrumGraphXbound,(int)hs),wt);
			else		  									drawDrumYAxis(g,new Dimension(DrumGraphXbound,(int)hs),null);
			g.translate(0, -(int)(i*hs + DrumGraphYTopMargin));
			
			g.translate(DrumGraphXbound, (int)(i*hs + DrumGraphYTopMargin));
			Dimension dme=new Dimension(dm.width-DrumGraphXbound,(int)hs);
			
			//　データを描画
			drawDrumData(g,dme,wt,ch,pro,DrawMode,center);
			// 時刻のドットを描画
			if(ShowTimeSamplePoint){
				drawDrumTimeDots(g,dme,wt);
			}
			g.translate(-DrumGraphXbound, -(int)(i*hs + DrumGraphYTopMargin));
		}
		drawDrumMarker(g, dm);
	}
	/**
	 * 時刻のドットを描画する
	 * @param g
	 * @param dm
	 * @param w
	 */
	private void drawDrumTimeDots(Graphics2D g,Dimension dm,wintime w){
		g.setColor(ColorTimeSample);
		float xstep = (float)(dm.width-DrumGraphXMargin) / (float)DrumTimeDivSub;
		for(int i=0;i<=DrumTimeDivSub;i++){
			g.drawLine((int)(i*xstep),dm.height-2,(int)(i*xstep),dm.height);
		}
		
	}
	/**
	 * カーソルを描くだけ
	 * @param g
	 * @param dm
	 * @param x
	 */
	private void drawDrumCursorSub(Graphics2D g, Dimension dm,int x){
		if(CursorDisplay!=0 && !IsPrint){
			Color colorg = g.getColor();
			g.setColor(ColorCursor);
			g.drawLine(x-2,-dm.height/2,x+2,-dm.height/2);
			g.drawLine(x,-dm.height/2,x,dm.height/2);
			g.drawLine(x-2,dm.height/2,x+2,dm.height/2);
			g.setColor(colorg);
		}
	}
	/**
	 * 時刻から座標を得る。（ｘ座標と行のインデックス)
	 * @param wt
	 *  時刻
	 * @return
	 * single DRUM Viewの座標 （Yは行のインデックス）
	 */
	private Point calcPointFromWinTime(wintime wt){
		long dispstart 	= DispTimeStartDrum.getValue();
		long curtime 	= wt.getValue();// + (float)wt.usec()/1000f;
		if(curtime<dispstart || curtime>dispstart + DispTimeRange_msDrum){
			return null; // 範囲外
		}
		float pos = curtime - dispstart;// 描画スタート時刻からの時間
		int line =  (int)(pos / ((float)DrumTimeSpanSec*1000)); // 何行目
		pos -= (line * (DrumTimeSpanSec*1000));
		pos = pos/(DrumTimeSpanSec*1000);
		int w = DrumRect.get(0).width;
		int w1 = w - DrumGraphXbound - DrumGraphXMargin;
		pos = w1 * pos;
		pos += DrumGraphXbound;
		
		Point p = new Point((int)pos,line);
		return p;
		
	}
	/**
	 *  DRUMモード　マーカー描画用の座標を計算する 
	 */
	private void calcDrumMarker(){
		// wintimeを得て
		wintime[] cwm = getDialogTimeMarker().getWinTime();
		// 座標に変換する
		TmMarker.clear();
		if(cwm!=null){
			if(cwm.length>=1){
				int[] idxmk = getDialogTimeMarker().getMarkerIndex();
				for(int i=0;i<cwm.length;i++){
					Point p = calcPointFromWinTime(cwm[i]);
					if(p==null){
						continue;
					}
					TimeMarker tm = new TimeMarker();
					tm.Number = idxmk[i];
					tm.Xpos = p.x;
					tm.Yline = p.y;
					//tm.IconRect = mr;
					TmMarker.add(tm);
				}
			}
		}
	}
	/**
	 * DRUMモードでマーカーを描画する
	 * @param g
	 * @param dm
	 * @param w
	 */
	private void drawDrumMarker(Graphics2D g,Dimension dm){
		Color org = g.getColor();
		Stroke strokeorg = g.getStroke();
		g.setStroke(Dashed1);

		float hs = (float)(dm.height - DrumGraphYbound - DrumGraphYTopMargin)/DrumTimeRowCount;

		Iterator<TimeMarker> ite = TmMarker.iterator();
		while(ite.hasNext()){
			TimeMarker tm = ite.next();
			if(tm.Number==0){	// 0番はリファレンスマーカー
				g.setColor(ColorMarker0);
			}
			else{
				g.setColor(ColorMarker1);
			}
			int line = tm.Yline;
			int y0 = (int)(line*hs + DrumGraphYTopMargin);
			int y1 = y0+(int)hs;
			g.drawLine(tm.Xpos,y0,tm.Xpos,y1);
			
			Font fonorg = g.getFont();
			Font fon2 = g.getFont().deriveFont(12f);
			g.setFont(fon2);
			g.setColor(ColorTextInfo);
			drawTools.drawText(g, String.format("[%d]", tm.Number), tm.Xpos+1, y1, drawTools.POSITION_LEFT_BOTTOM);
			g.setFont(fonorg);
		}
		g.setStroke(strokeorg);
		g.setColor(org);
	}
	/**
	 * データを描画する
	 * @param g
	 * @param dm
	 * @param wt
	 */
	private void drawDrumData(Graphics2D g,Dimension dm,wintime w,int ch,ChannelDataDisplayProperty pro,int drawmode,int center){
		
		int xsize = dm.width - DrumGraphXMargin; // ｘ方向のサイズ

		
		ChannelData d = null;
		wintime wts = new wintime(w);
		int start = 0;
		// 色を決める
		pro.setColorCalcMode( ColorCalcMode); // 色の計算モード
		Color dataCol = pro.getDataColor();
		if(ColorMode == COLOR_BW_BLACK_BACK || ColorMode == COLOR_BW_WHITE_BACK) dataCol = ColorDataBW; 
		g.setColor(dataCol);
		if(ClipRect){
			g.clipRect(0, 0, dm.width, dm.height);
		}
//		int center = 0;
//		if(drawmode==0 || drawmode == 1) {
//			center = (int)d.getMean();
//		}
//		else{
//			
//		}
		int ysize = dm.height;				// 1行の高さ
		float yspan = pro.getMax() - pro.getMin(); // 設定されたフルスパン
		// 中間をセンターにする
		g.translate(0,dm.height/2);
		int x2=0;
		int y2=0;
		//　描画を開始する時刻
		wintime wt = new wintime(wts);
		int mousetime = CurrentMouseTime.getValueS();
		int drawtime  = wt.getValueS();
		
		
		
		if(drawmode==0){
			// 全ポイントを描画
			ChannelDataCollection dcol = getChannelDataCollection(ch);
			for(start=0;start<DrumTimeSpanSec;start++,wts.inc_sec()){
				d = dcol.getChannelData(wts);
				if(d!=null) break;
			}
			if(d==null){
				g.translate(0,-dm.height/2);
				if(ClipRect){
					g.setClip(null);
				}
				return;
			}
			
			int[] dat = d.getData();
			int xnum0 = dat.length; 			// 1秒あたりのサンプル数
			int xnum = xnum0*(DrumTimeSpanSec); 	// 1行あたりのサンプル数
			
			DrawPoint=0;
			int xorg=0;
			int ymax=Integer.MIN_VALUE;
			int ymin=Integer.MAX_VALUE;
			boolean first = true;
			for(int i=start;i<DrumTimeSpanSec;i++,wt.inc_sec(),drawtime++){
				d = dcol.getChannelData(wt);
				if(d==null) continue;
				
				if(mousetime==drawtime){
					int x = (int)((float)(i*dat.length + 0)/(float)xnum * xsize);
					drawDrumCursorSub(g,dm,x);
				}

				dat = d.getData();
				DrawPoint+=dat.length;
				for(int j=0;j<dat.length;j++){
					int idata = dat[j] - center;
					int x = (int)((float)(i*dat.length + j)/(float)xnum * xsize);
					int y = -(int)((float)idata / yspan * ysize);
					if((j==0 && i==start) || first){// 先頭だけ
						x2=x; y2=y;
						xorg = x;
						ymax = y;
						ymin = y;
						first = false;
					}
					else{
						if(xorg == x){
							if(ymax<y) ymax=y;
							if(ymin>y) ymin=y;
						}
						else{
							xorg = x;
							g.drawLine(x2,	y2,		x,ymax);
							g.drawLine(x,	ymax,	x,ymin);
							x2=x; y2=ymin;
							ymax = y;
							ymin = y;
						}
					}
				}
			}
		}
		else if(drawmode==1){
			// 1秒で１ポイントを描画
			DrawPoint=0;
			int xorg=0;
			int ymax=Integer.MIN_VALUE;
			int ymin=Integer.MAX_VALUE;
			wt = new wintime(wts.year(),wts.month(),wts.day(),wts.hour(),wts.min(),wts.sec(),0);
			ChannelOutlineDataCollection  dc1 = DataBase.getChannelDataCollection1(ch) ;
			boolean first=true;
			for(int i=start;i<DrumTimeSpanSec;i+=1,wt.inc_sec(),drawtime++){
				ChannelOutlineData d1 = dc1.getChannelData(wt);
				if(d1==null){
					first=true;
					continue;
				}
				ChannelOutlineData cd  =  dc1.getChannelData(wt);
				int idata0 = cd.getMin() - center;
				int idata1 = cd.getMax() - center;
				int x = (int)((float)(i)/(float)DrumTimeSpanSec * xsize);
				int y0 = -(int)((float)idata0 / yspan * ysize);
				int y1 = -(int)((float)idata1 / yspan * ysize);
				if(first){
					x2=x; y2=y1;
					xorg = x;
					ymax = y1;
					ymin = y0;
					first = false;
				}
				else{
					if(xorg == x){
						if(ymax<y0) ymax=y0;
						if(ymin>y1) ymin=y1;
					}
					else{
						xorg = x;
						g.drawLine(x2,y2,	x,ymax);
						g.drawLine(x, ymax,	x,ymin);
						//x2=x; y2=y0;
						x2=x; y2=ymin;
						ymax = y1;
						ymin = y0;
					}
				}
			}
		}
		else if(drawmode==2){
			// 60秒で１ポイントを描画
			DrawPoint=0;
			int xorg=0;
			int ymax=Integer.MIN_VALUE;
			int ymin=Integer.MAX_VALUE;
			wt = new wintime(wts.year(),wts.month(),wts.day(),wts.hour(),wts.min(),0,0);
			ChannelOutlineDataCollection  dc60 = DataBase.getChannelDataCollection60(ch) ;
			boolean first=true;
			for(int i=start;i<DrumTimeSpanSec;i+=60,wt.inc_min(),drawtime++){
				ChannelOutlineData d60 = dc60.getChannelData(wt);
				if(d60==null){
					first=true;
					continue;
				}
				ChannelOutlineData cd  =  dc60.getChannelData(wt);
				int idata0 = cd.getMin() - center;
				int idata1 = cd.getMax() - center;
				int x = (int)((float)(i)/(float)DrumTimeSpanSec * xsize);
				int y0 = -(int)((float)idata0 / yspan * ysize);
				int y1 = -(int)((float)idata1 / yspan * ysize);
				if(first){
					x2=x; y2=y1;
					xorg = x;
					ymax = y1;
					ymin = y0;
					first = false;
				}
				else{
					if(xorg == x){
						if(ymax<y0) ymax=y0;
						if(ymin>y1) ymin=y1;
					}
					else{
						xorg = x;
						g.drawLine(x2,y2,	x,ymax);
						g.drawLine(x, ymax,	x,ymin);
						//x2=x; y2=y0;
						x2=x; y2=ymin;
						ymax = y1;
						ymin = y0;
					}
				}
			}
		}
		g.translate(0,-dm.height/2);
		if(ClipRect){
			g.setClip(null);
		}
	}
	/**
	 * Y軸とラベルを描画する
	 * @param g
	 * @param dm
	 * @param wt
	 */
	private void drawDrumYAxis(Graphics2D g,Dimension dm,wintime wt){
		g.setColor(ColorYAxis);
		g.drawLine(dm.width-13,dm.height/2,dm.width-10,dm.height/2);
		g.drawLine(dm.width-10,0,dm.width-10,dm.height);
		if(wt != null){
			String str = wt.fmtSec();
			drawTools.drawText(g, str, dm.width-13, dm.height/2, drawTools.POSITION_RIGHT_CENTER);
		}
	}
	/**
	 * 時間軸を描画する
	 * @param g
	 * @param dm
	 */
	private void drawDrumTimeAxis(Graphics2D g,Dimension dm){
		g.setColor(ColorXAxis);
		g.drawLine(0,0,dm.width-DrumGraphXMargin,0);
		float xstep = (float)(dm.width-DrumGraphXMargin) / (float)DrumTimeDiv;
		for(int i=0;i<=DrumTimeDiv;i++){
			g.drawLine((int)(i*xstep),0,(int)(i*xstep),4);
			drawTools.drawText(g, String.format("%02d",i*DrumTimeSec), (int)(i*xstep), 4, drawTools.POSITION_CENTER_TOP);
		}
		String mes=new String();
		if(DrumTimeSpanCode<=DrumTimeSpan3Hour){
			mes = "(sec)";
		}
		else{
			mes="(min)";
		}
		drawTools.drawText(g, mes, (int)(DrumTimeDiv*xstep), 16, drawTools.POSITION_RIGHT_TOP);
	}
	/**
	 * DispTimeStartとDispTimeRange_msをdrumに適するように修正する。
	 */
	private wintime DispTimeStartDrum = null;
	private long DispTimeRange_msDrum = 0;
	/**
	 * 時刻スパンの定義
	 */
	private static final int DrumTimeSpan1Minute 	= 0;
	private static final int DrumTimeSpan3Minute 	= 1;
	private static final int DrumTimeSpan10Minute 	= 2;
	private static final int DrumTimeSpan30Minute 	= 3;
	private static final int DrumTimeSpan1Hour 		= 4;
	private static final int DrumTimeSpan3Hour  	= 5;
	private static final int DrumTimeSpan12Hour 	= 6;
	private static final int DrumTimeSpan1Day 		= 7;
	private static final int DrumTimeSpan3Day 		= 8;
	private static final int DrumTimeSpan10Day 		= 9;
	private static final int DrumTimeSpanMax 		= 9;
	private int DrumTimeSpanCode 	= DrumTimeSpan10Minute;
//	private boolean DrumTimeTrancate       =false;
//	private boolean DrumTimeTrancateOrg 	= false;
	private boolean  DramTimeChangeFlag = true;
	/**
	 * 時刻をドラム記録方式用に変換する
	 */
	private void drumTimeManagement(){
		// 表示している最新の秒数
//		long latesttime = DispTimeStart.getValue() + DispTimeRange_ms;
//		latesttime = (latesttime)/1000L;
//		latesttime++;
//		// drumの表示スパン(秒）
//		long drummaxspan = DrumTimeSpanSec * DrumTimeRowCount;
//		// 最古時間を合わせる
//		long oldesttime = latesttime - drummaxspan;
		// １行の区切りに修正
		//wintime w = new wintime((int)oldesttime);
		wintime w = new wintime(GVDispTimeStart);
		wintime wm = null;
		int unitsec = 0;
		// 1行の時刻の切のいい時刻に丸める
		unitsec = DrumTimeSpanSec;
		if(unitsec < 60){
			wm = new wintime(w.year(),w.month(),w.day(),w.hour(),w.min(),unitsec*(w.sec()/unitsec),0);
			if(w.getValueS() != wm.getValueS()){
				wm.inc_sec(unitsec);
			}
		}
		else if(unitsec < 60*60){
			int min = unitsec/60;
			wm = new wintime(w.year(),w.month(),w.day(),w.hour(),min*(w.min()/min),0,0);
			if(w.getValueS() != wm.getValueS()){
				wm.inc_sec(unitsec);
			}
		}
		else if(unitsec < 60*60*24){
			int hour = unitsec/(60*60);
			wm = new wintime(w.year(),w.month(),w.day(),hour*(w.hour()/hour),0,0,0);
			if(w.getValueS() != wm.getValueS()){
				wm.inc_sec(unitsec);
			}
		}
		else{
			int day = unitsec/(60*60*24);
			wm = new wintime(w.year(),w.month(),day*(w.day()/day),0,0,0,0);
			if(w.getValueS() != wm.getValueS()){
				wm.inc_sec(unitsec);
			}
		}
		DispTimeStartDrum = wm;
		//DispTimeStartDrum.dec_sec((int)drummaxspan);
		if(DispTimeRange_msDrum != GVDispTimeRange_ms){
			int sec = (int)(GVDispTimeRange_ms/1000L);
			if(sec<=60){
				drumSetSpan(DrumTimeSpan1Minute);
			}
			else if(sec<=60*3){
				drumSetSpan(DrumTimeSpan3Minute);
			}
			else if(sec<=60*10){
				drumSetSpan(DrumTimeSpan10Minute);
			}
			else if(sec<=60*30){
				drumSetSpan(DrumTimeSpan30Minute);
			}
			else if(sec<=60*60){
				drumSetSpan(DrumTimeSpan1Hour);
			}
			else if(sec<=60*60*3){
				drumSetSpan(DrumTimeSpan3Hour);
			}
			else if(sec<=60*60*12){
				drumSetSpan(DrumTimeSpan12Hour);
			}
			else if(sec<=60*60*24){
				drumSetSpan(DrumTimeSpan1Day);
			}
			else if(sec<=60*60*24*3){
				drumSetSpan(DrumTimeSpan3Day);
			}
			else{
				drumSetSpan(DrumTimeSpan10Day);
			}
			
			//DispTimeRange_msDrum = DispTimeRange_ms;
		}
		if(ParentPanel!=null && DramTimeChangeFlag){
			ParentPanel.onChangeTimeStart(wm);
			ParentPanel.onChangeTimeRange((int)GVDispTimeRange_ms);
			//DispTimeRange_msDrum = DispTimeRange_ms;
			DramTimeChangeFlag=false;
		}

	}
	/**
	 * スパンを設定する
	 * @param code
	 */
	private void drumSetSpan(int code){
		if(code ==DrumTimeSpan10Day){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60*60*10;	// 時間スパン（1行分）
			DrumTimeDiv = 6;			// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv/60;		// １分割あたりの時間（分）
			DrumTimeRowCount = 24;		// 行数
			DrumTimeDivSub = 60;		// １行あたりの補助目盛の分割数
			DrumLabelStep = 3;
		}
		else if(code ==  DrumTimeSpan3Day){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60*60*3;	// 時間スパン（1行分）
			DrumTimeDiv = 6;			// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv/60;		// １分割あたりの時間（分）
			DrumTimeRowCount = 24;		// 行数
			DrumTimeDivSub = 60;		// １行あたりの補助目盛の分割数
			DrumLabelStep = 3;
		}
		else if(code ==  DrumTimeSpan1Day){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60*60;	// 時間スパン（1行分）
			DrumTimeDiv = 6;			// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv/60;		// １分割あたりの時間（分）
			DrumTimeRowCount = 24;		// 行数
			DrumTimeDivSub = 60;		// １行あたりの補助目盛の分割数
			DrumLabelStep = 3;
		}
		else if(code ==  DrumTimeSpan12Hour){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60*60;	// 時間スパン（1行分）
			DrumTimeDiv = 6;			// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv/60;		// １分割あたりの時間（分）
			DrumTimeRowCount = 12;		// 行数
			DrumTimeDivSub = 60;		// １行あたりの補助目盛の分割数
			DrumLabelStep = 3;
		}
		else if(code ==  DrumTimeSpan3Hour){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60*3*3;	// 時間スパン（1行分）
			DrumTimeDiv = 9;		    // １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv;		// １分割あたりの時間（秒）
			DrumTimeRowCount = 20;	// 行数
			DrumTimeDivSub = 60;	// １行あたりの補助目盛の分割数
			DrumLabelStep = 5;

		}
		else if(code ==  DrumTimeSpan1Hour){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60*3;	// 時間スパン（1行分）
			DrumTimeDiv = 9;		// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv;		// １分割あたりの時間（秒）
			DrumTimeRowCount = 20;	// 行数
			DrumTimeDivSub = 60;	// １行あたりの補助目盛の分割数
			DrumLabelStep = 5;

		}
		else if(code == DrumTimeSpan30Minute){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60*3;	// 時間スパン（1行分）
			DrumTimeDiv = 10;		// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv;		// １分割あたりの時間（秒）
			DrumTimeRowCount = 10;	// 行数
			DrumTimeDivSub = 100;	// １行あたりの補助目盛の分割数
			DrumLabelStep = 1;		// 時刻をなんこ飛ばしで表示するか
		}
		else if(code == DrumTimeSpan10Minute){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 60;	// 時間スパン（1行分）
			DrumTimeDiv = 10;		// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv;		// １分割あたりの時間（秒）
			DrumTimeRowCount = 10;	// 行数
			DrumTimeDivSub = 100;	// １行あたりの補助目盛の分割数
			DrumLabelStep = 1;		// 時刻をなんこ飛ばしで表示するか
		}
		else if(code == DrumTimeSpan3Minute){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 30;	// 時間スパン（1行分）
			DrumTimeDiv = 10;			// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv;		// １分割あたりの時間（秒）
			DrumTimeRowCount = 6;	// 行数
			DrumTimeDivSub = 100;	// １行あたりの補助目盛の分割数
			DrumLabelStep = 1;			// 時刻をなんこ飛ばしで表示するか
		}
		else if(code == DrumTimeSpan1Minute){
			DrumTimeSpanCode = code;
			DrumTimeSpanSec = 10;	// 時間スパン（1行分）
			DrumTimeDiv = 10;			// １行あたりの時間の分割数
			DrumTimeSec = DrumTimeSpanSec/DrumTimeDiv;		// １分割あたりの時間（秒）
			DrumTimeRowCount = 6;	// 行数
			DrumTimeDivSub = 100;	// １行あたりの補助目盛の分割数
			DrumLabelStep = 1;			// 時刻をなんこ飛ばしで表示するか
		}
		DispTimeRange_msDrum = DrumTimeSpanSec*DrumTimeRowCount*1000L;
		GVDispTimeRange_ms = DispTimeRange_msDrum;
		DramTimeChangeFlag=true;
	}
	/**
	 * 
	 * @param ch
	 * @param knet
	 */
	public void onAddKNETasciiInfo(int ch,KNET_ascii knet){
		ChannelDataDisplayProperty pro = getChannelInfo(ch);	
		if(pro == null){
			pro = new ChannelDataDisplayProperty(ch);
		}
		pro.Altitude 	= knet.Altitude;
		pro.Longitude  	= knet.Longitude;
		pro.Latitude  	= knet.Latitude;
		pro.unitString 	= knet.Unit;
		pro.unitFactor 	= knet.ScaleFactor;
		pro.FullScale   = knet.FullScale;
		pro.setStation(knet.StationCode);
		pro.setComponent(knet.Dir);
		ChConfig.setData(ch, pro);
	}
	/**
	 * ChannelDataCollection,ChannelDataCollection60を得る
	 * @param ch
	 * @param tm
	 * @param span
	 * @param datasource
	 * @return
	 * 0=みつからない、>0 = サンプルレート
	 */
	private Object getTypicalChannelDataCol(int ch,wintime tm,int span){
		// 最初には60秒データから探す
		wintime tm3 = new wintime(tm.year(),tm.month(),tm.day(),tm.hour(),tm.min(),0,0);
		for(int i=0;i<span/60;i++){
			ChannelOutlineDataCollection  dc60 = DataBase.getChannelDataCollection60(ch) ;
			if(dc60!=null){
				ChannelOutlineData cd  =  dc60.getChannelData(tm3);
				if(cd!=null){
					int samplerate = cd.getSamplerate(); 
					if(samplerate>0){
						return cd;
					}
				}
			}
			tm3.inc_min();
		}
		tm3 = new wintime(tm.year(),tm.month(),tm.day(),tm.hour(),tm.min(),tm.sec(),0);
		for(int i=0;i<span;i++){
			ChannelDataCollection  dc = DataBase.getChannelDataCollection(ch) ;
			if(dc!=null){
				ChannelData cd  =  dc.getChannelData(tm3);
				if(cd!=null){
					int samplerate = cd.getSamplerate(); 
					if(samplerate>0){
						return cd;
					}
				}
			}
			tm3.inc_sec();
		}
		return null;
	}
	public void onTabChanged() {
		ChConfig.save(INIT_FILE);
		if(DataBase!=null){
			DataBase.setDispRange(GVDispTimeStart, GVDispTimeRange_ms/1000, false);
		}
		
	}
	/**
	 * Manual Scale　ダイアログ
	 */
	private void doManualScale() {
		if(FixScaleDialog != null){
			if(FixScaleDialog.isShowing()){
				FixScaleDialog.dispose();
			}
		}
		FixScaleDialog = new DialogYFixScale(getParentFrame());
		FixScaleDialog.setParent(gv);

		Point p = getParentPoint();
		FixScaleDialog.setLocation(100+p.x+CurrentPopupPosition.x, 100+p.y+CurrentPopupPosition.y);
		
		// カーソル位置の
		ChannelDataDisplayProperty pro =getChannelDataDisplayProperty(CurrentPopupPosition);
		if(pro != null){
			pro.mode = ChannelDataDisplayProperty.MODE_FIX;
			ManualYScale.put(pro.getChannel(),pro);
			ChConfig.setData(pro.getChannel(), pro);
			FixScaleDialog.CenterValue  = (int)(pro.getCenterValue());
			FixScaleDialog.AmplitudeValue = (pro.getAmplitudeValue());
			FixScaleDialog.channel = pro.getChannel();
			FixScaleDialog.OffsetValue = pro.offset;
			FixScaleDialog.Unit = pro.unitString;
			FixScaleDialog.Factor = pro.unitFactor;
			FixScaleDialog.FullScale = pro.FullScale;
			FixScaleDialog.UnitType = pro.unitType;
			FixScaleDialog.setparam();
			FixScaleDialog.setVisible(true);
		}				
		repaintAll();
	}
	
}




